只使用一个dp阵列可以解决这个问题吗? 这是topcoder(http://community.topcoder.com/stat?c=problem_statement&pm=1259&rd=4493)的曲折问题 如果连续数字之间的差异在正数和负数之间严格交替,则数字序列称为Z字形序列。第一个差异(如果存在)可以是正面的也可以是负面的。少于两个元素的序列通常是一个Z字形序列。
例如,1,7,4,9,2,5是Z字形序列,因为差异(6,-3,5,-7,3)交替为正和负。相比之下,1,4,7,2,5和1,7,4,5,5不是Z字形序列,第一个是因为它的前两个差异是正的,第二个是因为它的最后差异是零。
给定一系列整数序列,返回序列的最长子序列的长度,该序列是Z字形序列。通过从原始序列中删除一些元素(可能为零)来获得子序列,将剩余的元素保留为原始顺序。
答案 0 :(得分:0)
供参考:具有两个数组的DP使用数组A [1..n],其中A [i]是以元素i上的zig结尾的之字形序列的最大长度,以及数组B [1]其中B [i]是以元素i上的zag结尾的之字形序列的最大长度。对于i从1到n,该DP使用A数组的先前条目来计算B [i],并使用B数组的先前条目来计算A [i]。以额外循环为代价,可以根据需要重新创建B条目,因此只使用A数组。不过,我不确定这是否能解决你的问题。
(另外,由于输入数组太短,因此有许多编码技巧都不值得一提。)
答案 1 :(得分:0)
这是一次尝试,我会从你的曲折处返回指数。在你的第二个输入(1,4,7,2,5)中,它返回5和4的索引,因为它是从4,7,2,5开始的锯齿形。
您可以根据结果判断整个阵列是否为锯齿形。
public class LongestZigZag
{
private readonly int[] _input;
public LongestZigZag(int[] input)
{
_input = input;
}
public Tuple<int,int> Sequence()
{
var indices = new Tuple<int, int>(int.MinValue, int.MinValue);
if (_input.Length <= 2) return indices;
for (int i = 2; i < _input.Length; i++)
{
var firstDiff = _input[i - 1] - _input[i - 2];
var secondDiff = _input[i] - _input[i - 1];
if ((firstDiff > 0 && secondDiff < 0) || (firstDiff < 0 && secondDiff > 0))
{
var index1 = indices.Item1;
if (index1 == int.MinValue)
{
index1 = i - 2;
}
indices = new Tuple<int, int>(index1, i);
}
else
{
indices = new Tuple<int, int>(int.MinValue, int.MinValue);
}
}
return indices;
}
}
答案 2 :(得分:0)
动态编程需要O(n 2 )时间来运行程序。我设计了一个线性时间复杂度为O(n)的代码。只需一次进入数组,它就会给出最大可能序列的长度。我已针对该问题测试了不同站点提供的许多测试用例,并取得了积极成果。
这是我的代码C实现:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i,j;
int n;
int count=0;
int flag=0;
scanf(" %d",&n);
int *a;
a = (int*)malloc(n*sizeof(a));
for(i=0;i<n;i++)
{
scanf(" %d",&a[i]); //1,7,5,10,13,15,10,5,16,8
}
i=0;
if(a[0] < a[1])
{
count++;
while(a[i] <= a[i+1] && i<n-1)
i++;
if(i==n-1 && a[i-1]<a[i])
{
count++;
i++;
}
}
while(i<n-1)
{ count++;
while(a[i] >= a[i+1] && i<n-1)
{
i++;
}
if(i==n-1 && a[i-1]>a[i])
{
count++;
break;
}
if(i<n-1)
count++;
while(a[i] <= a[i+1] && i<n-1)
{
i++;
}
if(i==n-1 && a[i-1]<a[i])
{
count++;
break;
}
}
printf("%d",count);
return 0;
}
答案 3 :(得分:0)
每一个(据我所知,所以不要理所当然)你用动态编程解决的解决方案归结为代表一个“解决方案空间”(意味着每个可能的解决方案都是正确的,不一定 DAG (有向无环图)。
例如,如果您正在寻找最长的上升子序列,那么解决方案空间可以表示为以下DAG:
e(u, v)
表示valueOf(u) < valueOf(v)
(其中valueOf(x)
是与节点x
关联的值)在动态编程中,找到问题的最佳解决方案与以正确的方式遍历此图表是一回事。该图提供的信息在某种意义上由该DP阵列表示。
在这种情况下,我们有两个订购操作。如果我们在其中一个图表上显示它们,那么该图形将不是非循环的 - 我们将需要至少两个图形(一个代表<
关系,一个代表>
)。
如果拓扑排序需要两个DAG,那么解决方案将需要两个DP阵列,或者一些巧妙的方式来指示你的DAG中的哪个边缘对应于哪个排序操作(在我看来这不必要地使问题复杂化)。
因此没有,你不能只使用一个DP阵列。您至少需要两个。至少如果你想要一个纯粹通过动态编程接近的简单解决方案。
这个问题的递归调用看起来应该是这样的(关系的方向可能是错误的,我还没有检查过):
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) + 1 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) + 1 if A[i] > A[j] for every 0 <= j < i)
这应该是O(n)具有正确的记忆(两个DP阵列)。这些调用返回解决方案的长度 - 只要找到最大值,就可以通过存储“父指针”来找到实际结果,然后在这些指针上向后遍历。