1. 程式人生 > >【專章】dp基礎

【專章】dp基礎

數量 cdr 入門 www tails 子序列 下一個 names pac

知識儲備:dp入門。

好了,完成了dp入門,我們可以做一些稍微不是那麽裸的題了。

------------------------------------------------------------------------------------

洛谷P1880 石子合並

分析:曾經的noi系列。石子合並問題分好幾種情況。

這題是環形的,且只能合並相鄰的兩堆,那麽我們把石子數組展開,如[1,2,3,4,5] -> [1,2,3,4,5,1,2,3,4,5],這樣進行處理,就能將環形轉換成直線。

轉換完之後,就可以用dp做了,dp的解釋詳見代碼。這題數據過得去,時間是 O(n3

) 不然要用平行四邊形優化。

安利一發acdreamer的博客,把石子合並問題講的很清楚http://blog.csdn.net/acdreamers/article/details/18039073

技術分享
 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 int sum[205],mins[205][205],maxs[205][205],s[205][205];//dp[i][j]=i~j的最大得分
 5 int main()
 6 {
 7     int  a,n,minnum=9999999
,maxnum=-minnum; 8 scanf("%d",&n); 9 for(int i=1;i<=n;i++) 10 { 11 scanf("%d",&sum[i]); 12 sum[i+n]=sum[i]; 13 s[i][i]=i; 14 s[i*2][i*2]=i; 15 } 16 for(int i=1;i<=2*n;i++) sum[i]+=sum[i-1]; //前綴和 17 for(int l=1;l<=n;l++)//
區間長度 18 { 19 for(int i=1;i+l<=2*n;i++)//開始的端點 20 { 21 22 int j=i+l;//結束的端點 23 maxs[i][j]=-99999999;mins[i][j]=-1*maxs[i][j]; 24 for(int k=i;k<j;k++) 25 { 26 maxs[i][j]=max(maxs[i][j],maxs[i][k]+maxs[k+1][j]+sum[j]-sum[i-1]); 27 mins[i][j]=min(mins[i][j],mins[i][k]+mins[k+1][j]+sum[j]-sum[i-1]); 28 } 29 } 30 } 31 for(int i=1;i<=n;i++)//因為是環形的,可從任意兩堆間分開,所以需要枚舉一遍起點從1~n 32 { 33 maxnum=max(maxs[i][i+n-1],maxnum); 34 minnum=min(mins[i][i+n-1],minnum); 35 } 36 printf("%d\n%d",minnum,maxnum); 37 return 0; 38 }
View Code

洛谷P1108 低價購買

分析:第一問還是很簡單的,求最長下降子序列。第二問要計算方案數量,還要判重。

用t[i]表示前i個股票的不同方案個數,可以得出,如果存在dp[j]==dp[i] && a[j]==a[i]方案就是重復的,所以就把t[i]賦為0,如果f[i]==f[j]+1,那麽i可以從j轉移,所以t[i]+=t[j]。最後統計和即可。

技術分享
 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 int dp[5005],a[5005],t[5005];//t,計算出現次數的dp 
 5 int main()
 6 {
 7     int ans=-99999,n,k=0,res=0;
 8     scanf("%d",&n);
 9     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
10     for(int i=1;i<=n;i++)
11     {
12         dp[i]=1;
13         for(int j=1;j<i;j++)
14         {
15             if(a[j]>a[i]) dp[i]=max(dp[i],dp[j]+1);
16         }
17         ans=max(ans,dp[i]);
18     }
19     for(int i=1;i<=n;i++)
20     {
21         if(dp[i]==1) t[i]=1;//如果無法轉移則為1 
22         for(int j=1;j<i;j++)
23         {
24             if(dp[j]==dp[i]-1 && a[i]<a[j])//判前繼 判可以為下一個數 
25             {
26                 t[i]+=t[j];//轉移
27             }
28             else if(dp[i]==dp[j]&&a[i]==a[j]) t[j]=0;//判重
29         }
30     }
31     for(int i=1;i<=n;i++)
32     {
33         if(dp[i]==ans) res+=t[i];
34     }
35     printf("%d %d",ans,res);
36     return 0;
37 }
View Code

【專章】dp基礎