Динамическое программирование: найдите самую длинную подпоследовательность, которая является зигзагом

Может ли кто-нибудь помочь мне понять основную логику решения проблемы, упомянутой в http://www.topcoder.com/stat?c=problem_statement&pm=1259&rd=4493

Последовательность zig zag представляет собой последовательность, которая попеременно увеличивается и уменьшается. Итак, 1 3 2 - зигзаг, но 1 2 3 нет. Любая последовательность одного или двух элементов - зигзаг. Нам нужно найти самую длинную подпоследовательность zig zag в данной последовательности. Подпоследовательность означает, что нет необходимости, чтобы элементы были смежными, как в самой продолжительной возрастающей проблеме подпоследовательности. Таким образом, 1 3 5 4 2 может иметь 1 5 4 в качестве подпоследовательности зигзага. Нам интересен самый длинный.

Я понимаю, что это проблема динамического программирования, и она очень похожа на Как определить самую длинную растущую подпоследовательность с помощью динамического программирования?.

Я думаю, что любому решению понадобится внешний цикл, который будет итерации по последовательностям разной длины, а внутренний цикл должен будет проходить по всем последовательностям.

Мы будем хранить самую длинную zig-zag-последовательность, заканчивающуюся индексом я в другом массиве, скажем, dpStore в индексе i. Таким образом, промежуточные результаты сохраняются и могут впоследствии использоваться повторно. Эта часть является общей для всех задач динамического программирования. Позже мы найдем глобальный максимум и вернем его.

Мое решение, безусловно, неверно, вставляя здесь, чтобы показать, что я до сих пор. Я хочу знать, где я ошибся.

    private int isZigzag(int[] arr)
{
    int max=0;
    int maxLength=-100;
    int[] dpStore = new int[arr.length];

    dpStore[0]=1;

    if(arr.length==1)
    {
        return 1;
    }
    else if(arr.length==2)
    {
        return 2;
    }
    else 
    {           
        for(int i=3; i<arr.length;i++)
        {
            maxLength=-100;
            for(int j=1;j<i && j+1<=arr.length; j++)
            {
                if(( arr[j]>arr[j-1] && arr[j]>arr[j+1])
                    ||(arr[j]<arr[j-1] && arr[j]<arr[j+1]))
                {
                    maxLength = Math.max(dpStore[j]+1, maxLength);
                }
            }
            dpStore[i]=maxLength;               
        }
    }
    max=-1000;
    for(int i=0;i<arr.length;i++)
    {
        max=Math.max(dpStore[i],max);
    }
    return max; 
}

Ответы

Ответ 1

Вот к чему связана ваша проблема:

Последовательность чисел называется зигзагообразной последовательностью, если различия между последовательными числами строго чередуются между положительными и отрицательными. Первое различие (если оно существует) может быть как положительным, так и отрицательным. Последовательность с менее чем двумя элементами тривиально представляет собой зигзагообразную последовательность.

Например, 1,7,4,9,2,5 является зигзагообразной последовательностью, поскольку различия (6, -3,5, -7,3) являются попеременно положительными и отрицательными. Напротив, 1,4,7,2,5 и 1,7,4,5,5 не являются зигзагообразными последовательностями, первый, потому что его первые два различия положительны, а во-вторых, потому что его последнее различие равно нулю.

Учитывая последовательность целых чисел, последовательность, возвращаем длину самой длинной последовательности последовательности, которая является зигзагообразной последовательностью. Подпоследовательность получается путем удаления некоторого количества элементов (возможно, нуля) из исходной последовательности, оставляя остальные элементы в исходном порядке.

Это полностью отличается от того, что вы описали в своем посте. Ниже решается актуальная проблема topcoder.

dp[i, 0] = maximum length subsequence ending at i such that the difference between the
           last two elements is positive
dp[i, 1] = same, but difference between the last two is negative

for i = 0 to n do     
   dp[i, 0] = dp[i, 1] = 1

   for j = 0 to to i - 1 do
    if a[i] - a[j] > 0
      dp[i, 0] = max(dp[j, 1] + 1, dp[i, 0])
    else if a[i] - a[j] < 0
      dp[i, 1] = max(dp[j, 0] + 1, dp[i, 1])

Пример:

i        = 0  1   2  3   4   5   6   7  8   9
a        = 1  17  5  10  13  15  10  5  16  8 
dp[i, 0] = 1  2   2  4   4   4   4   2  6   6    
dp[i, 1] = 1  1   3  3   3   3   5   5  3   7
           ^  ^   ^  ^
           |  |   |  -- gives us the sequence {1, 17, 5, 10}
           |  |   -- dp[2, 1] = dp[1, 0] + 1 because 5 - 17 < 0.
           |  ---- dp[1, 0] = max(dp[0, 1] + 1, 1) = 2 because 17 - 1 > 0
     1 element
   nothing to do
 the subsequence giving 7 is 1, 17, 5, 10, 5, 16, 8, hope I didn't make any careless
 mistakes in computing the other values)

Затем просто возьмите максимум обоих массивов dp.

Ответ 2

Это более простое решение

Пусть исходный массив A имеет длину n. Постройте другой массив B длиной n-1 только 0s и 1s. B [i] = 0, если a [i] -a [i + 1] >= 0 else B [i] = 1. Это можно сделать в O (n). Теперь у нас есть массив только 0s и 1, теперь проблема состоит в том, чтобы найти чередующиеся непрерывные 0s и 1s. Непрерывный массив массивных массивов в B из 0s будет представлен любым из его элементов. Например: Если B = [0,0,0,0,0, 1,0,0,0,1,0,1,1,1,0], то мы можем уменьшить B до Br, который = [0,1,0, 1,0,1,0] в O (n), на самом деле нам просто нужно найти размер Br, который можно сделать только с помощью одной итерации. И что мой друг - ответ на данную проблему. Таким образом, полная сложность O (n) + O (n) = O (n). Другими словами: Сохраните первый элемент. Затем найдите монотонную растущую или сокращающуюся часть последовательности и сохраните последний элемент из всех этих последовательностей.

UPDATE: вам нужно добавить один из ответов, выходящих из этого процесса, потому что вы считаете зигзаги, а не длину списка. Остерегайтесь проблемы с помехой: https://betterexplained.com/articles/learning-how-to-count-avoiding-the-fencepost-problem/

Ответ 3

Существует также жадный подход.

Возьмите первый элемент. Затем найдите минимальный или максимальный элемент в смежной последовательности, включая первый элемент, и выберите его.

То есть, если последовательность 1, 5, 7, 9, 2,4, сначала выберите 1, а затем 9, потому что 9 является максимумом в смежной последовательности 1, 5, 7, 9.

Действуйте аналогичным образом и выберите 2 и 5. Используя тот же подход, подпоследовательность, вычисленная для примера:

1, 17, 5, 10, 13, 15, 10, 5, 16, 8

: 1, 17, 5, 15, 5, 16, 8

Ответ 4

или вы можете использовать жадный алгоритм

public static int longestZigZag(int[] sequence) {
    if (sequence.length==1) return 1;
    if (sequence.length==2) return 2;
    int[] diff = new int[sequence.length-1];

    for (int i=1;i<sequence.length;i++){
        diff[i-1]=sequence[i]-sequence[i-1];
    }
    int prevsign=sign(diff[0]);
    int count=0;
    if (prevsign!=0)
        count=1;
    for (int i=1;i<diff.length;i++){
        int sign=sign(diff[i]);
        if (prevsign*sign==-1){
            prevsign=sign;
            count++;
        }
    }
    return count+1;
}

public static int sign(int a){
    if (a==0) return 0;
    return a/Math.abs(a);
}

Ответ 5

На самом деле, я считаю, что ответ с наивысшим балл правильный (IVlad's). Но я уверен, что часть динамического программирования (внешний контур) требуется не.

Используется жадный подход, и мы можем получить операции positive_end_seq[i] и negative_end_seq[i] с помощью операций:

    positive_end_seq[i] = negative_end_seq[i-1];
    negative_end_seq[i] = positive_end_seq[i-1];
    if (A[i-1] > A[i]) { // next element for positive_end_seq
       positive_end_seq[i] += 1; 
    }
    if (A[i-1] < A[i]) { // next element for negqtive_end_seq
       negative_end_seq[i] += 1;
    }
    // if (A[i-1] == A[i]) values don't change

positive_end_seq[0] = 1 и negative_end_seq[0] = 1, оба массива для всех i содержат длину самой длинной подпоследовательности с pos/neg, заканчивающийся на i -й элемент. Нам не нужно смотреть на элементы 0..i-2, и было бы неплохо доказать это.

Сложность времени O(n)

Конечно, массив pos/neg теперь можно заменить счетчиками, вот код в Java

    public static int subZigZag(int[] arr) {
      int pos_count = 1;
      int neg_count = 1;
      for(int i = 1; i < arr.length; ++i) {
        if (arr[i-1] < arr[i]) {
          pos_count = neg_count + 1;
        }
        if (arr[i-1] > arr[i]) {
          neg_count = pos_count+1;
        }
      }
      return Math.max(pos_count, neg_count);
    } 

Ответ 6

public static int longestZigZag(int[] sequence) {
    int max_seq = 0;

    if (sequence.length == 1) {
        return 1;
    }

    if (sequence.length == 1) {
        return 2;
    }

    int dp[] = new int[sequence.length];

    dp[0] = 1;
    dp[1] = 2;

    for (int i = 2; i < sequence.length; i++) {
        for (int j = i - 1; j > 0; j--) {
            if (((sequence[i] > sequence[j] &&
                sequence[j] < sequence[j - 1]) || 
                (sequence[i] < sequence[j] &&
                sequence[j] > sequence[j - 1])) &&
                dp[i] < dp[j] + 1) {
                dp[i] = dp[j] + 1;

                if (dp[i] > max_seq) {
                    max_seq = dp[i];
                }
            } 
        }
    }

    return max_seq;
}

Ответ 7

Это мой подход к простой жадной реализации.

Как уже упоминалось ранее, вам просто нужно посмотреть на zag'ing из трех последних точек.

def zigzag(xs):
    res = xs[:2]
    for x in xs[2:]:
        if cmp(res[-1], x) == cmp(res[-1], res[-2]):
            res.append(x)
        else:
            res[-1] = x
    return res

Ответ 8

Вот как это было в O (n)

public static int longestZigZag(int[] sequence) {
    if (sequence == null) {
        return 0;
    }

    int len  = sequence.length;
    if (len <= 2) {
        return len;
    }
    int minima = sequence[0];
    int maxima = sequence[0];
    int maximalen = 1;
    int minimalen = 1;

    for (int i = 1; i < len; i++) {
        if (sequence[i] < maxima) {
            if (minimalen < maximalen + 1) {
                minimalen = maximalen + 1;
                minima = sequence[i];
            } else if (minimalen == maximalen + 1 && sequence[i] < minima) {
                minima = sequence[i];
            }
        }
        if (sequence[i] > minima) {
            if (maximalen < minimalen + 1) {
                maximalen = minimalen + 1;
                maxima = sequence[i];
            } else if (maximalen == minimalen + 1 && sequence[i] > maxima) {
                maxima = sequence[i];
            }
        }
    }

    return Math.max(maximalen, minimalen);
}

Ответ 9

def ZigZag (tup):

length = len(tup)
lst = []
lst.append(1)
lst.append(2)
if length > 2:
    for i in range(2,length):
        if (tup[i]-tup[i-1]) * (tup[i-1]-tup[i-2]) < 0:
            d = lst[i-1] + 1
        else:
            d = lst[i-1]
        lst.append(d)

return lst[length-1]

Ответ 10

Выбирайте локальные максимумы и локальные минимумы, очень простые.

vector<int> longest_oscilating_subsequence(const vector<int> seq) {
    vector<int> result; // the resulting subsequence 

    for (int i = 0; i < seq.size(); ++i) {
        if (i > 0 && seq[i] == seq[i - 1]) continue;

        // is this point a local extreme 
        bool local_max = true, local_min = true;
        if (i > 0) {
            local_max = local_max && (seq[i] >= seq[i - 1]);
            local_min = local_min && (seq[i] <= seq[i - 1]);
        }
        if (i < seq.size() - 1) {
            local_max = local_max && (seq[i] >= seq[i + 1]);
            local_min = local_min && (seq[i] <= seq[i + 1]);
        }

        // potentially add it to the sequence 
        if (local_max || local_min) result.push_back(seq[i]);
    }

    return result; 
}