您的位置:首页 > 编程语言 > PHP开发

zoj2136 经典动态规划 求最长上升子序列

2009-07-10 20:09 239 查看
Longest Ordered Subsequence
题意:求一个数列的最长上升子序列。
输入:包括多组数据。每个数据包括两行,第一行整数N(1 <= N <= 1000)表示数列长度。第二行是N个0--10000之间的整数。
输出:每个数据输出一个整数,表示最长上升子序列的长度。

注意输入每组数据之间有空行。输出也一样。

Sample Input
1

 

7
1 7 3 5 9 4 8

 

Sample Output
4

题解:最简单的O(n^2)的算法是f[i]=max{f[j]+1}  ( j<i  ,  a[j]<a[i] )

代码如下

program z2136;
var t,n,i:integer;
a,f:array[1..1001] of integer;

procedure init;
var i:integer;
begin
readln(n);
for i:=1 to n do
begin
read(a[i]);
f[i]:=1;
end;
end;

procedure dp;
var i,j,maxx:integer;
begin
for i:=2 to n do
begin
maxx:=-maxint;
for j:=1 to i-1 do
if (f[j]+1>maxx)and(a[j]<a[i])
then maxx:=f[j]+1;
if maxx<>-maxint then f[i]:=maxx;
end;
end;

procedure print;
var i,maxL:integer;
begin
maxL:=-maxint;
for i:=1 to n do
if maxL<f[i] then maxL:=f[i];
writeln(maxL);
end;

begin
readln(t);
readln;
for i:=1 to t do
begin
init;
dp;
print;
if i<>t then
begin
writeln;
readln
end;
end;
end.


 

下面我们来看O(nlogn)的算法。

我看到一个很好理解的文章:http://blog.sina.com.cn/s/blog_575e6b9d010007cp.html

【解题思路】
一道典型的DP(动态规划)题。可以从两个方向着手,即:从左往右和从右往左。由于从右往左比较符合一般思想,故采用后者做算法示例。

首先我们设定一个flag[]来保存每种长度的上升串中的第一个元素值。比如上升串是2 5 8,那么在flag[3]这个位置应该存储2,即flag[i]表示i长度的上升串的首元素(该元素在上升串中值最小,且如果要增长这个串必须满足:新加入元素<flag[i])

对于一个原始串:1 7 3 5 9 4 8 来说,我们可以从右往左看(<-)。
1)首先遍历8,由于自身就是个上升串,故flag[1]=8。

2)其次映入眼帘的是4,这是4小于flag[1]=8,可以组成更长的上升串,故flag[2]=4,但此时我们不能取消掉flag[1]中的值8,比如在下列串中:5 6 7 4 8,后进入的7,明显可以组成比4-8更有“潜力”继续增长的,事实也证明,5 6 7 8比4 8更“长”,所以flag[1]=8我们将继续保留以备不时之需。

3)接下来读入的是9,它比任何一个flag[i]都大,其实我们不难发现flag[1]>flag[2]>flag[3]……flag
,公理上我就不证明了,因为flag[i+1]总是在flag[i]的基础上添加一个比flag[i]小的数得到的,所以上面的公式十分自然,因此我们只要比较出9>flag[1],就不必再比较后面了。这时,我们只需更新flag[1]=8为flag[i]=9,因为9比8更具备增长的潜力,比如前面还有个8,这是8-9就比8长(因为不能有8-8)。

4)再下来是5,我们发现flag[1]=9>5>flag[2]=4,在这种情况下,5-9相比4-8来说更具备增长潜力(因为5>4),所以flag[2]=4被更新为flag[2]=5。

5)碰到3的时候,因为3小于任何一个flag[i](除去初始化为0的那些flag[i]),扫描到flag[2]=5>3,且flag[3]=0时,我们将flag[3]赋值为3,这步与2)加入4的操作类似。

……………………
最后就得出了一个flag数组,其中flag[1]=9,flag[2]=7,flag[3]=3,flag[4]=1(其余flag[5]……flag
都=0,因为初始化为0)。其实,我们可以设置个max变量,在每次上升串增长的时候,比较新上升串是否>max,如果成立,则max=新增长的上升串的长度。

由此我们可以得出该从右向左扫描算法的一般表达式:
i)如果max=0(初始状态,表示扫描右边第一个元素),则flag[1]=in(in表示此时正扫描到的元素),max++;

ii)如果in>flag[1];则flag[i]=in;

iii)如果flag[i]>in>flag[i+1],且max不等于1时(也就是大于1),分两种情况:(1)flag[i+1]不为0时,即前面的例子中的第四步,flag[i+1]=in;(2)flag[i+1]=0时,也就是新扫描到的in小于每一个已经扫描过的元素,则flag[i+1]=in的同时,max还应该加1,也就是上例中的第二步和第五步。

最后还是要重申一点,如果在循环判断的过程中,出现flag[i]=in的情况下,直接放弃这次操作,不加处理。否则像1 2 2 3 3 4 4 这样的输出结果会成为7,而不是正确结果4。


代码正在实现中。。待续
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息