【解题报告】13级个人结业赛(二) ——动(dou)态(bu)规(hui)划(zuo)专场

额。果然是动(dou)态(bu)规(hui)划(zuo)专场。。。

A: 翻倍序列

dp[i][j]表示第i个位置是j的情况的个数
那么dp[i][j]=∑dp[i-1][k]   (j%k==0)
初始状态下dp[0][j]=1。(1<=j<=n)
最后要求的答案是∑dp[n-1][i]  (1<=i<=n)

可以先预处理好所有答案,然后询问的时候直接求和输出。

 1 #include<stdio.h>
 2 #include<math.h>
 3 #include<string.h>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<map>
 7 #include<set>
 8 #include<queue>
 9 #include<stack>
10 #define FOR(i,n) for(i=0;i<(n);i++)
11 #define CLR(a) memset(a,0,sizeof(a))
12 #define CIN(a) scanf("%d",&a)
13 typedef long long ll;
14 using namespace std;
15 int dp[2005][2005];
16 const int MOD=1000000007;
17 int main()
18 {
19     int n,k,i,j,kk;
20     n=2000,k=2000;
21     for(j=1;j<=n;j++) dp[0][j]=1;
22     for(i=0;i<k-1;i++){
23         for(j=1;j<=n;j++){
24             for(kk=j;kk<=n;kk+=j){
25                 dp[i+1][kk]=(dp[i+1][kk]+dp[i][j])%MOD;
26             }
27         }
28     }
29     while(scanf("%d%d",&n,&k)!=EOF) {
30         int ans=0;
31             for(j=1;j<=n;j++){
32                     ans=(ans+dp[k-1][j])%MOD;
33             }
34             printf("%d\n",ans);
35     }
36     return 0;
37 }
View Code

B: 汉诺塔

dp[k][i][j]表示把k个盘子从i移动到j所需要的最小花费。

首先z=3-i-j,表示除了i和j的另一个柱子

那么移动的时候有2种情况:
1.先把k-1个从i移动到z,然后把最大的那个从i移动到j,最后把z-1个从k移动到j
  也就是dp[k][i][j]=dp[k-1][i][z]+cost[i][j]+dp[k-1][z][j]

2.先把k-1个从i移动到j,然后把最大的那个从i移动到z,之后把k-1个从j移动回i,再把最大的从z移动到j,最后k-1个从i移动到j
  也就是dp[k][i][j]=dp[k-1][i][j]+cost[i][z]+dp[k-1][j][i]+cost[z][j]+dp[k-1][i][j]

然后两者取较小值。
最后要注意的是dp[0][i][j]=min{cost[i][j] ,   cost[i][z]+cost[z][j]}

 1 #include<stdio.h>
 2 #include<math.h>
 3 #include<string.h>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<map>
 7 #include<set>
 8 #include<queue>
 9 #include<stack>
10 #define FOR(i,n) for(i=0;i<(n);i++)
11 #define CLR(a) memset(a,0,sizeof(a))
12 #define CIN(a) scanf("%d",&a)
13 typedef long long ll;
14 using namespace std;
15 int cost[3][3];
16 ll DP[1000][3][3];
17 ll dfs(int n,int i,int j){
18     //printf("%d %d %d\n",n,i,j);
19     int k=3-i-j;
20     if(n==1) return min(cost[i][j],cost[i][k]+cost[k][j]);
21     if(DP[n][i][j]!=-1){
22         return DP[n][i][j];
23     }
24     ll ans1=0;
25     ans1=dfs(n-1,i,k);
26     ans1+=cost[i][j];
27     ans1+=dfs(n-1,k,j);
28  
29     ll ans2=0;
30     ans2=dfs(n-1,i,j);
31     ans2+=cost[i][k];
32     ans2+=dfs(n-1,j,i);
33     ans2+=cost[k][j];
34     ans2+=dfs(n-1,i,j);
35  
36     return DP[n][i][j]=min(ans1,ans2);
37 }
38 int main()
39 {
40     int n;
41     while(scanf("%d%d%d",&cost[0][0],&cost[0][1],&cost[0][2])!=EOF)
42     {
43         memset(DP,-1,sizeof(DP));
44         scanf("%d%d%d",&cost[1][0],&cost[1][1],&cost[1][2]);
45         scanf("%d%d%d",&cost[2][0],&cost[2][1],&cost[2][2]);
46         scanf("%d",&n);
47         printf("%lld\n",dfs(n,0,2));
48     }
49     return 0;
50 }
View Code

C: 红黑树

dp[i][0]表示有i个节点的树,如果根节点是黑色,有几种构造。
dp[i][1]表示有i个节点的树,如果根节点是红色,有几种构造。

根据题意可知,最后要求的就是dp[n][0]的值(根节点必须是是黑色)
dp[i][0]= dp[i/2+i%2][0]*dp[i/2][0]
     +dp[i/2+i%2][0]*dp[i/2][1]
     +dp[i/2+i%2][1]*dp[i/2][0]
     +dp[i/2+i%2][1]*dp[i/2][1]
dp[i][1]= dp[i/2+i%2][0]*dp[i/2][0]

dp[1][0]=1
dp[1][1]=0  (叶子节点不能是红色)

dp[2][1]=dp[2][0]=1

然后递推就行了。。

 1 #include<stdio.h>
 2 #include<math.h>
 3 #include<string.h>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<map>
 7 #include<set>
 8 #include<queue>
 9 #include<stack>
10 #define FOR(i,n) for(i=0;i<(n);i++)
11 #define CLR(a) memset(a,0,sizeof(a))
12 #define CIN(a) scanf("%d",&a)
13 typedef long long ll;
14 using namespace std;
15 const ll MOD=1000000007;
16 ll DP[1000005][2];
17 ll dfs(int x,int c)//x个节点,根节点颜色是c(0黑 1红)
18 {
19     //if(x<0) return 0;
20     if(x==1&&c==1) return 0;//叶子节点不能是红色
21     if(x==1) return 1;//叶子节点是黑色
22     if(DP[x][c]!=0) return DP[x][c];
23     ll ret=0,l,r;
24     if((x-1)%2) l=(x-1)/2+1;
25     else l=(x-1)/2;
26     r=(x-1)/2;
27     if(!c){//黑色
28         if(r!=0){
29             ret=(ret+(dfs(l,0)*dfs(r,1))%MOD)%MOD;
30             ret=(ret+(dfs(l,1)*dfs(r,0))%MOD)%MOD;
31             ret=(ret+(dfs(l,1)*dfs(r,1))%MOD)%MOD;
32             ret=(ret+dfs(l,0)*dfs(r,0)%MOD)%MOD;
33         }else{ret=1;}
34     }else{
35         if(r!=0){
36             ret=(ret+dfs(l,0)*dfs(r,0)%MOD)%MOD;
37         }else{ret=1;}
38     }
39     return DP[x][c]=ret;
40 }
41 int main()
42 {
43     int n;
44     memset(DP,0,sizeof(DP));
45     while(scanf("%d",&n)!=EOF){
46         printf("%lld\n",dfs(n,0));
47     }
48     return 0;
49 }
View Code

D: 01序列

dp[i][0]表示前i个字符组成的的子序列中 以0为结尾的01序列
dp[i][1]表示前i个字符组成的的子序列中 以1为结尾的01序列
dp[i][0]=dp[i-1][1]+1      dp[i][1]=dp[i-1][1]  (s[i]==0)
dp[i][1]=dp[i-1][0]+1      dp[i][0]=dp[i-1][0]  (s[i]==1)

把全部加起来就是答案了。

 1 #include<stdio.h>
 2 #include<math.h>
 3 #include<string.h>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<map>
 7 #include<set>
 8 #include<queue>
 9 #include<stack>
10 #define FOR(i,n) for(i=0;i<(n);i++)
11 #define CLR(a) memset(a,0,sizeof(a))
12 #define CIN(a) scanf("%d",&a)
13 typedef long long ll;
14 using namespace std;
15 int main()
16 {
17     int n,i;
18     int MOD=1000000007;
19     while(scanf("%d",&n)!=EOF){
20         ll ans=0;
21         ll a0=0,a1=0;
22         for(i=0;i<n;i++){
23             if(i%2){
24                 ans=(ans+(a0+1))%MOD;
25                 a1=(a1+a0+1)%MOD;
26             }else{
27                 ans=(ans+(a1+1))%MOD;
28                 a0=(a0+a1+1)%MOD;
29             }
30         }
31         printf("%lld\n",ans);
32     }
33     return 0;
34 }
View Code

E: 矩阵的最长不降子串

dp[i][j]表示以a[i][j]结尾的不递减子串最长的长度

那么dp[i][j]=max{
            if(a[i][j]>=a[i-1][j])   dp[i-1][j]+1
            if(a[i][j]>=a[i][j-1])   dp[i][j-1]+1
}
最后答案是最大的dp[i][j]

 1 #include<stdio.h>
 2 #include<math.h>
 3 #include<string.h>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<map>
 7 #include<set>
 8 #include<queue>
 9 #include<stack>
10 #define FOR(i,n) for(i=0;i<(n);i++)
11 #define CLR(a) memset(a,0,sizeof(a))
12 #define CIN(a) scanf("%d",&a)
13 typedef long long ll;
14 using namespace std;
15 int dp[1005][1005];
16 int a[1005][1005];
17 int n,m;
18 int main()
19 {
20     int i,j;
21     while(scanf("%d%d",&n,&m)!=EOF){
22         int ans=1;
23         for(i=0;i<n;i++){
24             for(j=0;j<m;j++){
25                 scanf("%d",&a[i][j]);
26                 dp[i][j]=1;
27                 if(i!=0&&a[i-1][j]<=a[i][j]){
28                     dp[i][j]=max(dp[i][j],dp[i-1][j]+1);
29                 }
30                 if(j!=0&&a[i][j-1]<=a[i][j]){
31                     dp[i][j]=max(dp[i][j],dp[i][j-1]+1);
32                 }
33                 ans=max(ans,dp[i][j]);
34             }
35         }
36         printf("%d\n",ans);
37     }
38     return 0;
39 }
View Code

 

posted on 2015-05-22 21:42  T^T  阅读(279)  评论(0编辑  收藏  举报

导航