Я пытался решить проблему зигзагообразных последовательностей на верхнем кодере. Временная сложность моего кода — O (n * n). Как я могу уменьшить его до O (n) или O (nlog (n))
Псевдокод или объяснение алгоритма будет очень полезным для меня
Вот постановка проблемы.
Постановка задачи
Последовательность чисел называется зигзагообразной последовательностью, если различия между последовательными числами строго чередуются между положительными и отрицательными. Первое различие (если оно существует) может быть положительным или отрицательным. Последовательность с менее чем двумя элементами тривиально является зигзагообразной последовательностью.
Например, 1,7,4,9,2,5 — зигзагообразная последовательность, потому что различия (6, -3,5, -7,3) попеременно положительны и отрицательны. Напротив, 1,4,7,2,5 и 1,7,4,5,5 не являются зигзагообразными последовательностями: первое, потому что первые два различия положительны, а второе, потому что последнее различие равно нулю.
Для заданной последовательности целых чисел последовательность возвращает длину самой длинной подпоследовательности последовательности, которая является зигзагообразной последовательностью. Подпоследовательность получается удалением некоторого количества элементов (возможно, нуля) из исходной последовательности, оставляя оставшиеся элементы в их первоначальном порядке.
И вот мой код
#include <iostream>
#include<vector>
#include<cstring>
#include<cstdio>
using namespace std;
class ZigZag
{
public:
int dp[200][2];
void print(int n)
{
for(int i=0;i<n;i++)
{
cout<<dp[i][0]<<endl;
}
}
int longestZigZag(vector<int> a)
{
int n=a.size();
//int dp[n][2];
for(int i=0;i<n;i++)
{
cout<<a[i]<<" "<<"\t";
}
cout<<endl;
memset(dp,sizeof(dp),0);
dp[0][1]=dp[0][0]=1;
for(int i=1;i<n;i++)
{
dp[i][1]=dp[i][0]=1;
for(int j=0;j<i;j++)
{
if(a[i]<a[j])
{
dp[i][0]=max(dp[j][1]+1,dp[i][0]);
}
if(a[j]<a[i])
{
dp[i][1]=max(dp[j][0]+1,dp[i][1]);
}
}
cout<<dp[i][1]<<"\t"<<dp[i][0]<<" "<<i<<endl;
//print(n);
}
cout<<dp[n-1][0]<<endl;
return max(dp[n-1][0],dp[n-1][1]);
}
};
Ты можешь сделать это в На) используя жадный подход. Возьмите первый неповторяющийся номер — это первый номер вашей зигзагообразной подпоследовательности. Проверьте, является ли следующее число в массиве меньше или больше первого числа.
Случай 1: Если меньший, отметьте следующий элемент и продолжайте, пока не найдете наименьший элемент (т.е.), элемент после которого будет больше, чем предыдущий элемент. Это будет ваш второй элемент.
Случай 2: Если большая, отметьте следующий элемент и продолжайте, пока не найдете самый большой элемент (т.е.), элемент после которого будет меньше, чем предыдущий элемент. Это будет ваш второй элемент.
Если вы использовали Случай 1, чтобы найти второй элемент, используйте Случай 2, чтобы найти третий элемент, или наоборот. Продолжайте чередовать эти два случая, пока у вас не будет больше элементов в исходной последовательности. Полученные в результате числа u образуют самую длинную зигзагообразную подпоследовательность.
Например: {1, 17, 5, 10, 13, 15, 10, 5, 16, 8}
Полученная подпоследовательность:
1 -> 1,17 (Дело 2) -> 1,17,5 (Дело 1) -> 1,17,5,15 (Дело 2) -> 1,17,5,15,5 (Дело 1) — > 1,17,5,15,5,16 (дело 2) -> 1,17,5,15,5,16,8 (дело 1)
Следовательно, длина самой длинной зигзагообразной подпоследовательности равна 7.
Вы можете обратиться к sjelkjd’s решение для реализации этой идеи.
Поскольку подпоследовательность не обязательно должна быть смежной, вы не можете сделать это O (n). В худшем случае сложность O (2 ^ n). Тем не менее, я сделал несколько проверок, чтобы отрезать поддеревья как можно скорее.
int maxLenght;
void test(vector<int>& a, int sign, int last, int pos, int currentLenght) {
if (maxLenght < currentLenght) maxLenght = currentLenght;
if (pos >= a.size() || pos >= a.size() + currentLenght - maxLenght) return;
if (last != a[pos] && (last - a[pos] >= 0) != sign)
test(a,!sign,a[pos],pos+1,currentLenght+1);
test(a,sign,last,pos+1,currentLenght);
}
int longestZigZag(vector<int>& a) {
maxLenght = 0;
test(a,0,a[0],1,1);
test(a,!0,a[0],1,1);
return maxLenght;
}
Ты можешь использовать RMQs удалить внутренний цикл for. Когда вы найдете ответ для dp[i][0]
а также dp[i][1]
, сохранить его в двух деревьях RMQ — скажем, RMQ0 и RMQ1 — так же, как вы делаете сейчас с двумя рядами dp
массив. Итак, когда вы рассчитываете dp[i][0]
, вы ставите значение dp[i][0]
на позиции a[i]
в RMQ0, Это означает, что существует зигзагообразная последовательность с длиной dp[i][0]
все больше и больше a[i]
,
Затем для того, чтобы рассчитать dp[i + 1][0]
Вам не нужно перебирать все числа от 0 до i
, Вместо этого вы можете запросить RMQ0 для наибольшего числа на позиции> a[i + 1]
, Это даст вам самую длинную зигзагообразную подпоследовательность, заканчивающуюся числом, превышающим текущую, то есть самую длинную, которая может продолжаться по убыванию с номером. a[i + 1]
, Тогда вы можете сделать то же самое для RMQ1 для другой половины зигзагообразных подпоследовательностей.
Так как вы можете реализовать динамический RMQ со сложностью запроса O(log N)
это дает вам общую сложность O(N log N)
,
Вы можете решить эту проблему в O(n)
время и O(n)
дополнительное пространство
Алгоритм работает следующим образом.
n-1
return (result+1)
Вот его реализация в C ++
#include <iostream>
#include <vector>
using namespace std;
int main()
{
ios_base::sync_with_stdio(false);
int n;
cin>>n;
vector<int> data(n);
for(int i = 0; i < n; i++)
cin>>data[i];
vector<int> diff(n-1);
for(int i = 1; i < n; i++)
diff[i-1] = data[i]-data[i-1];
int res = 1;
if( n < 2)
cout<<res<<"\n";
else
{
int temp_idx = 0;
for(int i = 1; i < n-1; i++)
{
if(diff[i]*diff[i-1] < 0)
{
temp_idx++;
res++;
}
else
{
res = max(res,temp_idx);
temp_idx = 1;
}
}
cout<<res+1<<"\n";
}
return 0;
}
Это чисто теоретическое решение. Вот как бы вы решили это, если бы вас попросили об этом в академической среде, стоящей рядом с классной доской.
Решение проблемы может быть создано с помощью динамического программирования:
Подзадача имеет вид: если у меня есть элемент x последовательности, какая самая длинная подпоследовательность заканчивается на этом элементе?
Затем вы можете разработать свое решение с помощью рекурсивных вызовов, которые должны выглядеть примерно так (направления отношений могут быть неправильными, я не проверял это):
S - given sequence (array of integers)
P(i), Q(i) - length of the longest zigzag subsequence on elements S[0 -> i] inclusive (the longest sequence that is correct, where S[i] is the last element)
P(i) = {if i == 0 then 1
{max(Q(j) if A[i] < A[j] for every 0 <= j < i)Q(i) = {if i == 0 then 0 #yields 0 because we are pedantic about "is zig the first relation, or is it zag?". If we aren't, then this can be a 1.
{max(P(j) if A[i] > A[j] for every 0 <= j < i)
Это должно быть O(n)
с правильным запоминанием (сохраняя каждый вывод Q (i) и P (i)), потому что каждая подзадача вычисляется только один раз: n*|P| + n*|Q|
,
Эти вызовы возвращают длину решения — фактический результат может быть найден путем хранения «родительского указателя» всякий раз, когда найдено максимальное значение, а затем обратного хода по этим указателям.
Вы можете избежать рекурсии, просто заменив вызовы функций поисками в массиве: P[i]
а также Q[i]
и используя for
петля.