(Это попытка сделать N.log(N) наихудший случай. К сожалению, это неправильно — иногда он недосчитывается. Он ошибочно предполагает, что вы можете найти все блоки, просматривая только соседние пары меньших допустимых блоков. На самом деле у вас есть смотреть на тройки, четверки и т. д., чтобы получить все большие блоки.)
Вы делаете это со структурой, которая представляет подблок и очередь для подблоков.
struct
c_subblock
{
int index ; /* index into original array, head of subblock */
int width ; /* width of subblock > 0 */
int lo_value;
c_subblock * p_above ; /* null or subblock above with same index */
};
Выделите массив подблоков того же размера, что и исходный массив, и инициализируйте каждый подблок так, чтобы в нем был ровно один элемент. Добавляйте их в очередь по ходу. Если вы начнете с массива [ 7 3 4 1 2 6 5 8 ], вы получите такую очередь:
очередь: ( [7,7] [3,3] [4,4] [1,1] [2,2] [6,6] [5,5] [8,8] )
Значения { index, width, lo_value, p_above } для подблока [7,7] будут {0, 1, 7, null}.
Теперь это легко. Простите псевдокод c-ish.
loop {
c_subblock * const p_left = Pop subblock from queue.
int const right_index = p_left.index + p_left.width;
if ( right_index < length original array ) {
// Find adjacent subblock on the right.
// To do this you'll need the original array of length-1 subblocks.
c_subblock const * p_right = array_basic_subblocks[ right_index ];
do {
Check the left/right subblocks to see if the two merged are also a subblock.
If they are add a new merged subblock to the end of the queue.
p_right = p_right.p_above;
}
while ( p_right );
}
}
Это найдет их всех, я думаю. Обычно это O (N log (N)), но это будет O (N ^ 2) для полностью отсортированного или антисортированного списка. Я думаю, что на этот вопрос есть ответ: когда вы строите исходный массив подблоков, вы ищете отсортированные и антисортированные последовательности и добавляете их в качестве подблоков базового уровня. Если вы ведете счет, увеличьте его на (ширина * (ширина + 1))/2 для базового уровня. Это даст вам счет, ВКЛЮЧАЯ все подблоки длины 1.
После этого просто используйте описанный выше цикл, выталкивая и проталкивая очередь. Если вы считаете, вам нужно будет иметь множитель как для левого, так и для правого подблоков и перемножить их вместе, чтобы вычислить приращение. Множитель — это ширина самого левого (для p_left) или самого правого (для p_right) подблока базового уровня.
Надеюсь, это понятно и не слишком глючит. Я просто стучу, так что это может быть даже неправильно. [Позднее примечание. Это не работает в конце концов. См. примечание ниже.]
person
nealabq
schedule
27.07.2010
O(N^2)
, потому что в тривиальном случае отсортированной последовательности вы получитеO(N^2)
результатов. Интересный вопрос. На ум приходит динамическое программирование или разделяй и властвуй. - person Hamish Grubijan   schedule 27.07.2010