• 【解题报告】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
    ",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,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
    ",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
    ",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
    ",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
    ",ans);
    37     }
    38     return 0;
    39 }
    View Code
  • 相关阅读:
    操作系统-多进程图像
    025.Kubernetes掌握Service-SVC基础使用
    Linux常用查看版本指令
    使用动态SQL处理table_name作为输入参数的存储过程(MySQL)
    INTERVAL 用法 mysql
    sql server编写archive通用模板脚本实现自动分批删除数据【填空式编程】
    docker部署redis集群
    Ubuntu1804下安装Gitab
    Bash脚本编程学习笔记06:条件结构体
    KVM虚拟化基础
  • 原文地址:https://www.cnblogs.com/syiml/p/4523285.html
Copyright © 2020-2023  润新知