• 动态规划专题uva


    uva 10891 Game of Sum

    题目大意:两个人轮流从数组里面取数,可以在头或者尾选择连续的任意多个,直到取完。最后统计两个人取走的数之和,两个人都尽量让自己的得分高,求A的得分减去B的得分。

    分析:这题的关键是只能在头尾取,把数组看成一个序列(i~j),那么取走k个数后仍是一个连续的序列(i+k,j)或(i,j-k)。

    我们用dp[i][j]表示碰到(i,j)这个序列能得到的最高分数。

    那么我们考虑如何转移,这里有点博弈的思想,我们要现在这个状态最高即要求对方碰到的状态(即自己取完后的状态)的得分越少越好。

    那么状态转移方程就是dp[i][j]=sum[i][j]-min{dp[i+1][j]....dp[j][j],dp[i][j-1]....dp[i][i],0}。

     1 /*dp[i][j]=sum[i][j]-min{dp[i+1][j]....dp[j][j],dp[i][j-1]....dp[i][i],0}
     2  *  
     3 */
     4 #include <iostream>
     5 #include <cstdio>
     6 #include <cstring>
     7 #define maxlen 110
     8 using namespace std;
     9 int dp[maxlen][maxlen];
    10 int sum[maxlen];//the sum of 0~i
    11 bool used[maxlen][maxlen];
    12 int min(int a,int b)
    13 {
    14     return a<b?a:b;
    15 }
    16 int dfs(int i,int j)//dp search
    17 {
    18     if(used[i][j])
    19         return dp[i][j];
    20     used[i][j]=true;
    21     int ans=0;
    22     for(int k=i+1;k<=j;++k)
    23         ans=min(ans,dfs(k,j));
    24     for(int k=j-1;k>=i;--k)
    25         ans=min(ans,dfs(i,k));
    26     return dp[i][j]=sum[j]-sum[i-1]-ans;
    27 }
    28 int main ()
    29 {
    30     int n;
    31     while(scanf("%d",&n)!=EOF)
    32     {
    33         if(n==0)
    34             break;
    35         sum[0]=0;
    36         for(int i=1;i<=n;++i)
    37         {
    38             int x;
    39             scanf("%d",&x);
    40             sum[i]=sum[i-1]+x;
    41         }
    42         memset(used,false,sizeof(used));
    43         int ans=2*dfs(1,n)-sum[n];
    44         printf("%d
    ",ans);
    45     }    
    46 }
    View Code

     uva 11584 Partitioning by Palindromes

    题目大意:给你一个字符串,把它划分为回文串,问最小的回文串个数

    分析:dp[i]表示字符串(1,i)的最小回文串个数

    状态转移就是枚举子集,求一个最小值

    方程为:dp[i]=min{dp[j-1]+1,dp},j<=i&&字符串(j,i)为回文串

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #define maxlen 1010
     6 #define INF 0x3fffff
     7 using namespace std;
     8 int dp[maxlen];
     9 char str[maxlen];
    10 int min(int a,int b)
    11 {
    12     return a<b?a:b;
    13 }
    14 bool judge(int i,int j)
    15 {
    16     while(i<j)
    17     {
    18         if(str[i]!=str[j])
    19             return false;
    20         i++;
    21         j--;
    22     }
    23     return true;
    24 }
    25 int main()
    26 {
    27     int t;
    28     scanf("%d",&t);
    29     gets(str);
    30     while(t--)
    31     {
    32         gets(str+1);
    33         int len=strlen(str+1);
    34         for(int i=1;i<=len;++i)
    35             dp[i]=INF;
    36         dp[1]=1;
    37         for(int i=1;i<=len;++i)
    38         {
    39             for(int j=1;j<=i;++j)
    40             {
    41                 if(judge(j,i))
    42                     dp[i]=min(dp[i],dp[j-1]+1);
    43             }
    44         }
    45         printf("%d
    ",dp[len]);
    46     }
    47 }
    View Code

     uva 10405 Longest Common Subsequence

    题目大意:最经典的LCS问题,最长公共子序列。

    dp[i][j]表示str匹配到i,str2匹配到j时的最大长度。

    状态转移方程为:

    d[i][j]=dp[i-1][j-1]+1 (str[i]==str2[j])

    dp[i][j]=max(dp[i-1][j],dp[i][j-1])(str[i]!=str2[j])

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #define maxlen 1010
     5 using namespace std;
     6 int dp[maxlen][maxlen];
     7 char str[maxlen];
     8 char str2[maxlen];
     9 int main ()
    10 {
    11     while(gets(str))
    12     {
    13         gets(str2);
    14         int len=strlen(str);
    15         int len2=strlen(str2);
    16         memset(dp,0,sizeof(dp));
    17         for(int i=1;i<=len;++i)
    18         {
    19             for(int j=1;j<=len2;++j)
    20             {
    21                 if(str[i-1]==str2[j-1])
    22                     dp[i][j]=dp[i-1][j-1]+1;
    23                 else 
    24                     dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
    25             }
    26         }
    27         printf("%d
    ",dp[len][len2]);
    28     } 
    29 }
    View Code

     uva 674 Coin Change

    题目大意:有50,25,10,5,1五种硬币,现在给你n问有多少种兑换方式

    分析:

    dp[i]表示兑换种数

    状态方程就是dp[i]=sum{dp[i-num[j]]}(0<=j<5&&i>=num[j])

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #define maxlen 7500
     5 using namespace std;
     6 int dp[maxlen];
     7 int num[]={50,25,10,5,1};
     8 int main ()
     9 {
    10     int n;
    11     dp[0]=1;
    12     for(int i=0;i<5;++i)
    13     {
    14         for(int j=0;j<maxlen;++j)
    15         {
    16             if(j>=num[i])
    17                 dp[j]+=dp[j-num[i]];
    18         }
    19     }
    20     while(scanf("%d",&n)!=EOF)
    21     {  
    22         printf("%d
    ",dp[n]);
    23     }
    24 }
    View Code

     uva 10003 Cutting Sticks

    题目大意:给你长度n的木棍,以及m个要切割的位置。问如何安排顺序是的花费最小,花费的定义是每次切割时木棍的长度之和。

    分析:本题最主要的是状态的划分,一开始不知道如何下手,本来想从起始两端的位置dp,后来发现没用。

    后来想到用切割的位置来dp

    dp[i][j]表示在已有的切割要求下完成切割所需要的最小代价,区间i, j表示第i个和第j个切割点,初始为0,结束为最后一个节点,之间为输入切割点数

    切割点的位置是一定的,但是切割的时候剩余的木块长度不一定,所以有

    dp[i,j] = min{ dp[i, k] + dp[k, j] } + num[j] - num[i]    (i<k<j) 

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #define maxlen 60
     5 #define INF 0x3fffff
     6 using namespace std;
     7 int dp[maxlen][maxlen];
     8 int num[maxlen];
     9 int dfs(int i,int j)
    10 {
    11     if(i==j-1)
    12         return dp[i][j]=0;
    13     if(dp[i][j]!=-1)
    14         return dp[i][j];
    15     dp[i][j]=INF;
    16     for(int k=i+1;k<j;++k)
    17         dp[i][j]=min(dp[i][j],dfs(i,k)+dfs(k,j)+num[j]-num[i]);
    18     return dp[i][j];
    19 }
    20 int main()
    21 {
    22     int n,m;
    23     while(scanf("%d",&n)!=EOF)
    24     {
    25         if(n==0)break;
    26         scanf("%d",&m);
    27         for(int i=1;i<=m;++i)
    28             scanf("%d",&num[i]);
    29         memset(dp,-1,sizeof(dp));
    30         num[0]=0;
    31         num[m+1]=n;
    32         int ans=dfs(0,m+1);
    33         printf("The minimum cutting is %d.
    ",ans);
    34     }
    35 }
    View Code

     uva 116 Unidirectional TSP

    题目大意:给以n*m矩阵,要求求从0列到m-1列的一条路径使得和最小,在行上面是循环的即最后一行向下走一步就回到第一行

    方向有三个:

    picture25

     要求输出字典序最小的路径以及最小值

    分析:dp[i][j]表示走到(i,j)位置的最小值,那么它是由三个状态dp[(i-1)%n][j+1],dp[i][j+1],dp[(i+1)%n][j]转化过来的,三者取一下最小值然后记录路径即可。

    方程为:dp[i][j]=min{dp[(i-1)%n][j+1],dp[i][j+1],dp[(i+1)%n][j]}+map[i][j]

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #define maxlen 210
     5 #define INF 0x3ffffff
     6 #define min(a,b)(a<b?a:b)
     7 using namespace std;
     8 int dp[maxlen][maxlen],path[maxlen][maxlen];
     9 int maps[maxlen][maxlen];
    10 int n,m;
    11 void init()
    12 {
    13     memset(dp,0,sizeof(dp));
    14     memset(path,0,sizeof(path));
    15     memset(maps,0,sizeof(maps));
    16 }
    17 int main ()
    18 {
    19     while(scanf("%d%d",&n,&m)!=EOF)
    20     {
    21         init();
    22         for(int i=0;i<n;++i)
    23             for(int j=0;j<m;++j)
    24                 scanf("%d",&maps[i][j]);
    25         for(int j=m-1;j>=0;--j)
    26         {
    27             for(int i=0;i<n;++i)
    28             {
    29                 int m=min(dp[(i-1+n)%n][j+1],min(dp[i][j+1],dp[(i+1)%n][j+1]));
    30                 dp[i][j]=maps[i][j]+m;
    31                 path[i][j]=INF;
    32                 if(m==dp[(i-1+n)%n][j+1])
    33                     path[i][j]=min(path[i][j],(i-1+n)%n);
    34                 if(m==dp[i][j+1])//这里不能写成else if
    35                     path[i][j]=min(path[i][j],i);
    36                 if(m==dp[(i+1)%n][j+1])
    37                     path[i][j]=min(path[i][j],(i+1)%n);
    38             }
    39         }
    40         int ans=INF;
    41         int c;
    42         for(int i=0;i<n;++i)
    43         {
    44             if(ans>dp[i][0])
    45             {
    46                 c=i;
    47                 ans=dp[i][0];
    48             }
    49         }
    50         printf("%d",c+1);
    51         c=path[c][0]; 
    52         for(int j=1;j<m;++j)
    53         {
    54             printf(" %d",c+1);
    55             c=path[c][j];
    56         }
    57         printf("
    ");
    58         printf("%d
    ",ans);
    59     }
    60 }
    View Code

     uva 10066 The Twin Towers

    题目大意:最长公共子序列

    方程见上面的,是差不多的(一样的)。

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #define maxlen 110
     5 #define INF 0x3ffffff
     6 #define max(a,b)(a>b?a:b)
     7 using namespace std;
     8 int dp[maxlen][maxlen];
     9 int num[maxlen];
    10 int num2[maxlen];
    11 int n,m;
    12 int main ()
    13 {
    14     int Case=1;
    15     while(scanf("%d%d",&n,&m)!=EOF)
    16     {
    17         if(n==0&&m==0)
    18             break;
    19         for(int i=1;i<=n;++i)
    20             scanf("%d",&num[i]);
    21         for(int i=1;i<=m;++i)
    22             scanf("%d",&num2[i]);
    23         memset(dp,0,sizeof(dp));
    24         for(int i=1;i<=n;++i)
    25         {
    26             for(int j=1;j<=m;++j)
    27             {
    28                 if(num[i]==num2[j])
    29                     dp[i][j]=dp[i-1][j-1]+1;
    30                 else 
    31                     dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
    32             }
    33         }
    34         printf("Twin Towers #%d
    ",Case++);
    35         printf("Number of Tiles : %d
    
    ",dp[n][m]);
    36     }
    37 }
    View Code

     uva 357 Let Me Count The Ways

    跟上面的钱币兑换是一样的,注意要用longlong 

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #define maxlen 30010
     5 using namespace std;
     6 long long  dp[maxlen];
     7 int num[]={50,25,10,5,1};
     8 int main ()
     9 {
    10     int n;
    11     dp[0]=1;
    12     for(int i=0;i<5;++i)
    13     {
    14         for(int j=0;j<maxlen;++j)
    15         {
    16             if(j>=num[i])
    17                 dp[j]+=dp[j-num[i]];
    18         }
    19     }
    20     while(scanf("%d",&n)!=EOF)
    21     {
    22         if(dp[n]==1)
    23             printf("There is only 1 way to produce %d cents change.
    ",n);
    24         else  
    25             printf("There are %lld ways to produce %d cents change.
    ",dp[n],n); 
    26     }
    27 }
    View Code

     uva 562 Dividing coins

    题目大意:把一些钱num[]分给两个人,要求尽量使两个人得到的钱只差最小,输出最小的差值、

    分析:我们这么考虑这个问题,num[i]只能分给一个人,0表示分给第一个人,1表示分给第二个人,那么可以从0-1背包方向考虑。

    假设总钱数为sum且A分得的钱不超过B,dp[i]表示A是否可以分得一些硬币使得其总钱数为i,dp[i]为1表示可以,0表示不可以。

    那么就是0-1背包问题的变形了

    dp[0]=1。然后分别对每个硬币枚举,判断dp是否为1即可。最后选择离Sum/2最近且dp[i]==1的i即可。

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #define maxlen 50010
     5 using namespace std;
     6 bool dp[maxlen];
     7 int num[110];
     8 int n;
     9 int main()
    10 {
    11     int t;
    12     scanf("%d",&t);
    13     while(t--)
    14     {
    15         scanf("%d",&n);
    16         int sum=0;
    17         for(int i=0;i<n;++i)
    18         {
    19             scanf("%d",&num[i]);
    20             sum+=num[i];
    21         }
    22         memset(dp,0,sizeof(dp));
    23         dp[0]=true;
    24         for(int i=0;i<n;++i)
    25         {
    26             for(int j=sum;j>=num[i];--j)
    27             {
    28                 if(!dp[j])
    29                     dp[j]=dp[j-num[i]];
    30             }
    31         }
    32         for(int i=sum/2;i>=0;--i)
    33         {
    34             if(dp[i])
    35             {
    36                 printf("%d
    ",sum-i-i);
    37                 break;
    38             }
    39         }
    40     }
    41 }
    View Code

     uva 10130 SuperSale

    题目大意:每种物品有价值与重量,现在有n个人,给出每个人能承受的最大重量,问这些人最多能够拿到的物品最大值是多少。

    分析:多人的0-1背包,只是对每个人用0-1背包求最大值然后加起来就可以了。

    使用滚动数组可以把二维的优化到一维。dp[i]表示容量i能装下的最大价值

    状态转移方程维:

    dp[i]=max{dp[i],dp[i-w]+v} if (i>=w)

    dp[i]=dp[i] (i<w)初始化为0

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #define maxlen 1010
     5 using namespace std;
     6 int w[maxlen],v[maxlen],dp[maxlen];
     7 int n,g;
     8 int main ()
     9 {
    10     int t;
    11     scanf("%d",&t);
    12     while(t--)
    13     {
    14         scanf("%d",&n);
    15         memset(dp,0,sizeof(dp));
    16         for(int i=1;i<=n;++i)
    17         {
    18             scanf("%d%d",&v[i],&w[i]);
    19             for(int j=30;j>=0;--j)
    20             {
    21                 if(j>=w[i])
    22                     dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    23             }
    24         }
    25         scanf("%d",&g);
    26         int ans=0;
    27         int x;
    28         for(int i=0;i<g;++i)
    29         {
    30             scanf("%d",&x);
    31             ans+=dp[x];
    32         }
    33         printf("%d
    ",ans);
    34     }
    35 }
    View Code

     uva 624 CD

    分析:0-1背包,可以理解为价值与重量是一样的0-1背包,需要记录路径。

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #define maxlen 2010
     5 using namespace std;
     6 int v[30],dp[maxlen];
     7 bool path[30][maxlen];
     8 int V,n;
     9 int main ()
    10 {
    11     while(scanf("%d%d",&V,&n)!=EOF)
    12     {
    13         for(int i=1;i<=n;++i)
    14             scanf("%d",&v[i]);
    15         memset(dp,0,sizeof(dp));
    16         memset(path,0,sizeof(path));
    17         for(int i=n;i>=1;--i)
    18         {
    19             for(int j=V;j>=v[i];--j)
    20             {
    21                 if(j>=v[i]&&dp[j]<dp[j-v[i]]+v[i])
    22                 {
    23                     dp[j]=dp[j-v[i]]+v[i];
    24                     path[i][j]=true;
    25                 }
    26             }
    27         }
    28         for(int i=1,j=V;i<=n;++i)
    29         {
    30             if(path[i][j])
    31             {
    32                 printf("%d ",v[i]);
    33                 j-=v[i];
    34             }
    35         }
    36         printf("sum:%d
    ",dp[V]);
    37     }
    38 }
    View Code

     uva 147 Dollars

    分析:跟上面的钱币兑换类似,这里有个优化的就是题目说的是5的倍数,那么每个都除以5,会使程序速度更快。

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #define maxlen 20010
     5 using namespace std;
     6 long long  dp[maxlen];
     7 int cc[]={1,2,4,10,20,40,100,200,400,1000,2000};
     8 int n;
     9 int main()
    10 {
    11     int i,j,k;
    12     double num;
    13     while (scanf("%lf",&num),num>0) 
    14     {
    15         n=int((num+0.005)*100);
    16         n/=5;
    17         memset(dp,0,sizeof(dp));
    18         dp[0]=1;
    19         for (int i=0;i<=10;++i) 
    20         {
    21             for (int j=0;j<=n;++j)
    22             {
    23                 if(j>=cc[i])
    24                     dp[j]+=dp[j-cc[i]];               
    25             }
    26         }
    27         printf("%6.2lf%17lld
    ",num,dp[n]);
    28     }
    29 }
    View Code
  • 相关阅读:
    学习网站
    支付宝调用错误:Call to undefined function openssl_sign()
    打包APP
    PHP 转义
    tp5学习
    js函数
    php学习随笔--定时触发
    day07
    60.函数应用:学生管理系统
    59.列表推导式
  • 原文地址:https://www.cnblogs.com/shuzy/p/3286772.html
Copyright © 2020-2023  润新知