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 }