最大上升子序列

最长上升子序列

1、子问题:求以ak(k=1, 2, 3…N)为终点的最长上升子序列的长度(一个上升子序列中最右边的那个数,称为该子序列的 “终点”)

2、确定状态:子问题只和一个变量--数字的位置相关。

因此序列中数的位置k就是“状态”,而状态 k 对应的“值”,就是以ak 做为“终点”的最长上升子序列的长度。

状态一共有N个。

3、状态转移方程:

maxLen (1) = 1【初始状态】

maxLen (k) = max { maxLen (i):1<=i < k 且 ai < ak且 k≠1 } + 1【若找不到这样的i,则maxLen(k) = 1】

maxLen(k)的值,就是在ak左边,“终点”数值小于ak ,且长度最大的那个上升子序列的长度再加1。

因为ak左边任何“终点”小于ak的子序列,加上ak后就能形成一个更长的上升子序列。


“人人为我”递推型动归程序

[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstring>  
  3. #include<algorithm>  
  4. using namespace std;  
  5.   
  6. const int MAXN=1010;  
  7. int a[MAXN];  
  8. int maxlen[MAXN];  
  9.   
  10. int main()  
  11. {  
  12.     int n;  
  13.     cin>>n;  
  14.     for(int i=1; i<=n; ++i)  
  15.     {  
  16.         cin>>a[i];  
  17.         maxlen[i]=1;  
  18.     }  
  19.     for(int i=2; i<=n; ++i)  
  20.         for(int j=1; j<i; ++j)  
  21.         {  
  22.             if(a[i]>a[j])  
  23.                 maxlen[i]=max(maxlen[i],maxlen[j]+1);  
  24.         }  
  25.     cout<<*max_element(maxlen+1,maxlen+n+1);  
  26.     return 0;  
  27. }  


“我为人人”递推型动归程序

[cpp]  view plain  copy
  1. <strong><span style="font-size:12px;color:#006600;"><span style="color:#006600;"><strong><span style="color:#000000;">/*#include<iostream> 
  2. #include<cstring> 
  3. #include<algorithm> 
  4. using namespace std; 
  5.  
  6. const int MAXN=1010; 
  7. int a[MAXN]; 
  8. int maxlen[MAXN]; 
  9.  
  10. int main() 
  11. { 
  12.     int n; 
  13.     cin>>n; 
  14.     for(int i=1; i<=n; ++i) 
  15.     { 
  16.         cin>>a[i]; 
  17.         maxlen[i]=1; 
  18.     }*/  
  19.     for(int i=1; i<=n; ++i)  
  20.         for(int j=i+1; j<=n; ++j)//看看能更新哪些状态的值  
  21.         {  
  22.             if(a[j]>a[i])  
  23.                 maxlen[j]=max(maxlen[j],maxlen[i]+1);  
  24.         }  
  25.     /*cout<<*max_element(maxlen+1,maxlen+n+1); 
  26.     return 0; 
  27. }*/</span>  
  28. </strong></span></span></strong>  

进一步,区分动规的3种形式

1)记忆递归型

优点:只经过有用的状态,没有浪费。递推型会查看一些没用的状态,有浪费

缺点:可能会因递归层数太深导致爆栈,函数调用带来额外时间开销。无法使用滚动数组节省空间。总体来说,比递推型慢。

==>人人为我==>我为人人

2)“人人为我”递推型:状态i的值Fi由若干个值已知的状态值Fk ,Fm ,..Fy推出,如求和,取最大值

在选取最优备选状态的值Fm,Fn,…Fy时,有可能有好的算法或数据结构可以用来显著降低时间复杂度。

3)“我为人人”递推型:状态i的值Fi在被更新(不一定是最终求出)的时候,

依据Fi去更新(不一定是最终求出)和状态i相关的其他一些状态的值Fk ,Fm ,..Fy

没有什么明显的优势,有时比较符合思考的习惯。个别特殊题目中会比“人人为我”型节省空间。


一个补充:min_element()、max_element()和nth_element()

头文件:#include<algorithm>

作用:返回容器中最小值和最大值。max_element(first,end,cmp);//其中cmp为可选择参数???

[cpp]  view plain  copy
  1. #include<iostream>    
  2. #include<algorithm>    
  3. using namespace std;    
  4. bool cmp(int a,int b)    
  5. {    
  6.       return a<b;    
  7. }    
  8. int main()    
  9. {    
  10.       int num[]={2,3,1,6,4,5};    
  11.       cout<<"最小值是 "<<*min_element(num,num+6)<<endl;    
  12.       cout<<"最大值是 "<<*max_element(num,num+6)<<endl;    
  13.       cout<<"最小值是 "<<*min_element(num,num+6,cmp)<<endl;    
  14.       cout<<"最大值是 "<<*max_element(num,num+6,cmp)<<endl;    
  15.       return 0;     
  16. }  


POJ1458 Common Subsequence 最长公共子序列 http://poj.org/problem?id=1458

输入两个串s1,s2,

MaxLen(i,j):s1的左边i个字符形成的子串,与s2左边的j个字符形成的子串的最长公共子序列的长度(i,j从0开始算)

MaxLen(i,j) 就是本题的“状态”

假定 len1 = strlen(s1),len2 = strlen(s2),那么题目就是要求 MaxLen(len1,len2)

显然,

MaxLen(n,0) = 0 ( n=0…len1)

MaxLen(0,n) = 0 ( n=0…len2)

递推公式:

if ( s1[i-1] == s2[j-1] ) //s1的最左边字符是s1[0] 

MaxLen(i,j) = MaxLen(i-1,j-1) + 1;

else

MaxLen(i,j) = Max(MaxLen(i,j-1),MaxLen(i-1,j) );

【时间复杂度O(mn)

[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstring>  
  3. #include<algorithm>  
  4. using namespace std;  
  5.   
  6. char s1[1000];  
  7. char s2[1000];  
  8. int maxlen[1000][1000];  
  9.   
  10. int main()  
  11. {  
  12.     while(cin>>s1>>s2)  
  13.     {  
  14.         int len1=strlen(s1);  
  15.         int len2=strlen(s2);  
  16.         int ntmp;  
  17.         for(int i=0;i<=len1;i++)  
  18.             maxlen[i][0]=0;  
  19.         for(int j=0;j<=len2;j++)  
  20.             maxlen[0][j]=0;  
  21.         for(int i=1;i<=len1;i++)  
  22.             for(int j=1;j<=len2;j++)  
  23.         {  
  24.             if(s1[i-1]==s2[j-1])//  
  25.                 maxlen[i][j]=maxlen[i-1][j-1]+1;  
  26.             else maxlen[i][j]=max(maxlen[i][j-1],maxlen[i-1][j]);  
  27.         }  
  28.         cout<<maxlen[len1][len2]<<endl;  
  29.     }  
  30.     return 0;  
  31. }  

猜你喜欢

转载自blog.csdn.net/summerone123/article/details/79330362