动态规划专题(一) HDU1087 最长公共子序列
Super Jumping! Jumping! Jumping!
首先对于动态规划问题要找出其子问题,如果找的子问题是前n个序列的最长上升子序列,但这样的子问题不好,因为它不具备无后效性,因为它的第n+1的数会影响前n个序列的长度,换句话说,如果第n+1个数加上去不一定使得和前n个数加起来就是最长子序列,具体例子很多比如5,6,1,2 第5个数是3,那么最长序列5,6加3不会比1,2加3长。
比较好的子问题是“求以第K个为终点的最长上升子序列”,其实这个子问题的好处在于它限定了一点,终点为第k个数,那么我去递推第K+1的最长子序列时,它只跟前面各个以某点为终点的最长子序列有关
接下来,我们写出它的状态转移方程
maxLen(k)表示为ak作为终点的最长上升子序列的长度
初始状态:maxLen(1)=1
maxLen(k)=max{ maxLen(i):1<=i<k且ai < ak 且k>=2}+1
若找不到则maxLen(k)=1
因为以小于ak为终点的各序列,若满足上述条件,加上ak,一定会形成更长的上升子序列。
另外从认识的角度讲,ak是从终点1到k-1一个个判断下来的,那么一旦他们两个是上升的,连带的会把ai的最长子序列长度带上去,使之变得更长。
本题仅作了一个小的改动最长上升总和,思路大致相同,代码如下
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define MAXN 1005 5 using namespace std; 6 int num[MAXN],m[MAXN];//m记录以每个终点的最长上升总和 7 int main() 8 { 9 int t,i,j,k; 10 while(cin>>t) 11 { 12 if(t==0) 13 break; 14 memset(m,0,sizeof(0)); 15 for(i=1;i<=t;i++) 16 scanf("%d",&num[i]); 17 m[1]=num[1]; 18 for(i=2;i<=t;i++) 19 { 20 m[i]=num[i]; 21 for(j=1;j<i;j++) 22 { 23 if(num[j]<num[i]) 24 m[i]=max(m[i],m[j]+num[i]); 25 } 26 } 27 k=m[1]; 28 for(i=1;i<=t;i++) 29 k=m[i]>k?m[i]:k; 30 cout<<k<<endl; 31 } 32 return 0; 33 }