您的位置:首页 > 其它

动态规划总结

2009-08-07 19:34 387 查看
已知10,000个整数的序列(整数大于0小于100,000),求最长递增子序列。注意,子序列不一定非得连续。以下是我们常用的三种dp方法,但是时间效率相差之大却是惊人的,所以在利用dp的同时一定要采用最好的办法。

1.递归下降解法

#include<iostream>
using namespace std;
int m;
int main()
{
int check (int start,int nmatches,int smallest,int*a) ;
int i,*a,best;
while(cin>>m)
{
a=new int[m];
for(i=0;i<m;i++)
cin>>a[i];
best=check(0,0,0,a);
cout<<best<<endl;
}
return 0;
}
int check (int start,int nmatches,int smallest,int *sequence)
{
int better, i, best=nmatches;
for (i=start;i<m;i++)
{
if (sequence[i]>smallest)
{
better=check (i,nmatches+1,sequence[i],sequence);
if (better>best) best=better;
}
}
return best;
}

这样的dp是我会首先想到的,也是我最常用的,用check递归调用,但是却经常会碰到超时的问题。

N Seconds
60 0.130
70 0.360
80 2.390
90 13.190

2.从末端开始

逆向的思维,从序列的末端开始进行,将bestfor数组标记为1,我们首先考虑序列的最后两个元素。如果倒数第二个数字比最后那个小,那么‘bestsofar’为2(1+最后那个数字的‘bestsofar’)。否则为1。这样我们就可以考虑在最后两个元素之前的任一元素,如果该元素num[j]比其后的元素小,并且bestsofar[j]<bestsofar[i],那么我们就可以把bestsofarp[j]=bestsofar[i]+1;

#include <stdio.h>
#define MAXN 10000
void main ()
{
long num[MAXN], bestsofar[MAXN];
long n, i, j, longest;
while(scanf( "%ld", &n)!=EOF)
{
longest=0;
for (i = 0; i < n; i++) scanf("%ld", &num[i]);
bestsofar[n-1] = 1;
for (i = n-1-1; i >= 0; i--)
{
bestsofar[i] = 1;
for (j = i+1; j < n; j++)
{
if (num[j]>num[i] && bestsofar[j] >= bestsofar[i])
{
bestsofar[i] = bestsofar[j] + 1;
if (bestsofar[i] > longest) longest = bestsofar[i];
}
}
}
printf("%d\n", longest);
}
}

以‘i’循环从后计数和‘j’循环从前计数的巧妙直接的方法,运行O(N2)算法。尽管比前面的那个算法多1行,运行时间却更佳:
N Secs
1000 0.080
2000 0.240
3000 0.550
4000 0.950
5000 1.450
6000 2.080
7000 2.990
8000 3.700
9000 4.700
10000 6.330
11000 7.350

当N=9000时由于内部循环,算法就很慢。一组不同的值最好存在辅助数组中。数组‘bestrun’的指针是最长子序列的长度,其值是率领子序列的第一个(也被证明是最佳的)整数。执行该数组。当遇到一个整数比在这个数组中的值小时就意味着可能会产生一个新的更长的序列。新整数可能是一个新的“序列最佳首部”,也可能不是。看下面这个序列:
10 8 9 4 6 3
从右向左浏览(从后向前),遇到3之后‘bestrun’数组有且只有一个元素:
0:3
继续浏览,6比3大,‘bestrun’数组变成:
0:3
1:6

4不比6大,但它比3大,所以数组变为:

0:3
1:4

9使数组增大为:

0:3
1:4
2:9

8同4使数组变成:

0:3
1:4
2:8

l0再次扩增数组为:

0:3
1:4
2:8
3:10

于是结果出来了:4(数组中有4个元素)。

因为‘bestrun’数组可能比处理过的序列长度增长得慢,这个算法可能比上一个运行得要快。实际上是增速很多。代码如下:

#include <stdio.h>
#define MAXN 10000
void main ()
{
long num[MAXN], bestrun[MAXN];
long n, i, j, highestrun ;
while((scanf("%ld", &n))!=EOF)
{
highestrun = 0;
for (i=0; i<n; i++)
scanf( "%ld", &num[i]);
bestrun[0] = num[n-1];
highestrun = 1;
for (i = n-1-1; i >= 0; i--)
{
if (num[i]> bestrun[0])
{
bestrun[0] = num[i];
continue;
}
for (j = highestrun - 1; j >= 0; j--)
{
if (num[i] <bestrun[j]) {
if (j == highestrun - 1 || num[i] > bestrun[j+1]){

bestrun[++j] = num[i];
if (j == highestrun) highestrun++;
break;
}
}
}
}
printf("%d\n", highestrun);
}
}

N orig Improved
1000 0.080 0.030
2000 0.240 0.030
3000 0.550 0.050
4000 0.950 0.060
5000 1.450 0.080
6000 2.080 0.090
7000 2.990 0.110
8000 3.700 0.130
9000 4.700 0.140
10000 6.330 0.160
11000 7.350 0.170
20000 0.290
40000 0.570
60000 0.910
80000 1.290
100000 2.220
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: