[关闭]
@Yeasion-Nein 2018-09-09T14:25:14.000000Z 字数 15390 阅读 1137

更好的阅读体验

最近要写计划,当然,就是讲自己的弱项之类的写上去,脑子一抽写了个九月份之前题,现在就只能橙黄相伴了.....现在是把做过的DP的题写一下题解报告吧......

妖塔的建造很特别,塔总共有层,但是高度却不相同,这造成了小爬过每层的时间也不同.小会用仙术,每用一次可以让他向上跳一层或两层,但是每次跳跃后小都将用完灵力,必须爬过至少一层才能再次跳跃(你可以认为小需要跳两次一层才休息),小想用最短的时间爬到塔顶,可是他不能找到时间最短的方案,所以请你帮他找到一个时间最短的方案让他爬到塔顶,小只关心时间,所以你只要告诉他最短时间是多少就可以了.你可以最后跳到塔外即超过塔高.

输入输出格式

输入格式:

第一行一个数,表示塔的层数.

第二行个数,表示从下往上每层的高度.

输出格式:

一个数,表示最短时间.

首先,按照DP的常规思路,我们先找到子问题:

为爬上第层的最短时间。那么我们可以很容易地确定出子问题:

1.是从层爬上第层的
2.是用魔法飞上第层的

我们知道爬上第层肯定是要从第层,即.

然后至于第二种情况,我们又要分两种情况:1.从层跳上来,2.从层跳上来,时间要取最小值,那么就是

下面是代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define MAXN 10010
  6. using namespace std;
  7. int n,dp[MAXN];
  8. int main(){
  9. scanf("%d",&n);
  10. for(int i=1;i<=n;i++)
  11. scanf("%d",&dp[i]);
  12. for(int i=1;i<=n+1;i++)
  13. dp[i]=min(dp[i-1],min(dp[i-2],dp[i-3]))+dp[i];
  14. //对于所有的情况,直接取min就好了。
  15. printf("%d",dp[n+1]);
  16. return 0;
  17. }

是两个字符串。我们要用最少的字符操作次数,将字符串转换为字符串。这里所说的字符操作共有三种:

1、删除一个字符;
2、插入一个字符;
3、将一个字符改为另一个字符;
(!皆为小写字母!)

输入输出格式

输入格式:

第一行为字符串;第二行为字符串;字符串的长度均小于

输出格式:

只有一个正整数,为最少字符操作次数。

子问题确定:对于一个状态我们只有四种操作方式:删除、插入、改变、不变。所以状态就是由前一个状态进行这四步操作转化过来的,所以我们的自问题也就是个。

首先我们设为字符串的前个字符变为字符串的前个字符需要多少步,然后最后的答案显然就是;

那么再根据子问题分开来谈状态转移方程。

删除。我们直接选择删除最后一个,那么也就变成了的前个字符变为的前个字符需要多少步,删除需要步数。即;

添加。我们选择在的最后面添加一个字符,那么肯定是为了使最后一个字符相匹配,那么最后一个字符也就匹配上可以删去,那么我们可以理解为,那么就是咯。同样,添加也需要步数;

替换,我们替换字符串的最后一个字符为的最后一个字符,那么的最后一位显然必定也是匹配上可以删除了,那么都要减一,同样步数要,即:;

不变。字符串的最后一个字符都相等,那么自然不要变咯,同样删除最后一个字符,但是步数并不用,因为我们并没有进行操作,那么方程就是;

然后最后的状态转移方程就是:

当然,这个是要看的最后一个字符是不是相等。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define MAXN 2010
  6. using namespace std;
  7. string a,b;
  8. char s1[MAXN],s2[MAXN];
  9. int f[MAXN][MAXN];
  10. int DP(int i,int j){
  11. if(f[i][j]!=-1) return f[i][j];
  12. if(i==0) return f[i][j]=j;
  13. if(j==0) return f[i][j]=i;
  14. int ken=1;
  15. if(s1[i]==s2[j]) ken=0;
  16. return f[i][j]=min(min(DP(i-1,j)+1,DP(i,j-1)+1),DP(i-1,j-1)+ken);
  17. }
  18. int main(){
  19. cin>>a>>b;
  20. int len1=a.length();
  21. int len2=b.length();
  22. memset(f,-1,sizeof(f));
  23. for(int i=1;i<=len1;i++)
  24. s1[i]=a[i-1];
  25. for(int i=1;i<=len2;i++)
  26. s2[i]=b[i-1];
  27. DP(len1,len2);
  28. printf("%d",f[len1][len2]);
  29. return 0;
  30. }

很少有人知道奶牛爱吃苹果。农夫约翰的农场上有两棵苹果树(编号为), 每一棵树上都长满了苹果。奶牛贝茜无法摘下树上的苹果,所以她只能等待苹果 从树上落下。但是,由于苹果掉到地上会摔烂,贝茜必须在半空中接住苹果(没有人爱吃摔烂的苹果)。贝茜吃东西很快,她接到苹果后仅用几秒钟就能吃完。每一分钟,两棵苹果树其中的一棵会掉落一个苹果。贝茜已经过了足够的训练, 只要站在树下就一定能接住这棵树上掉落的苹果。同时,贝茜能够在两棵树之间 快速移动(移动时间远少于分钟),因此当苹果掉落时,她必定站在两棵树其中的一棵下面。此外,奶牛不愿意不停地往返于两棵树之间,因此会错过一些苹果。苹果每分钟掉落一个,共分钟,贝茜最多愿意移动 次。现给出每分钟掉落苹果的树的编号,要求判定贝茜能够接住的最多苹果数。 开始时贝茜在号树下。

输入输出格式

输入格式:

第一行个数,。接下来的行,每行一个数,代表在时刻苹果是从号苹果树还是从号苹果树上掉下来的。

输出格式:

对于每个测试点,输出一行,一个数,为奶牛最多接到的苹果的数量。

照常,我们首先找出子问题。

表示最多走i步,时刻处于第棵树下的最多苹果数

表示最多走i步,时刻处于第棵树下的最多苹果数

子问题一共分种情况。

如果从第棵树掉下来,并且最后一次移动在时刻,那么;

如果从第棵树掉下来,并且最后一次移动不在时刻,那么;

如果从第棵树掉下来,并且最后一次移动在时刻,那么;

如果从第棵树掉下来,并且最后一次移动在不时刻,那么;

我们知道初始时刻我们在第棵树下,那么从头开始如果一直是从掉下,那么我们肯定就是一直不用动,就会一直++,知道出现第一个从掉下的苹果为止,而这个就是初始化。

那么最后的答案就是;

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define MAXN 1010
  6. using namespace std;
  7. int a[MAXN],dp[MAXN][MAXN][2];
  8. int n,w;
  9. int main(){
  10. scanf("%d%d",&n,&w);
  11. for(int i=1;i<=n;i++){
  12. scanf("%d",&a[i]);
  13. a[i]--;
  14. }
  15. for(int i=1;i<=n;i++){
  16. dp[i][0][0]=dp[i-1][0][0]+(a[i]^1);
  17. for(int j=1;j<=w;j++){
  18. dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j-1][1])+(a[i]^1);
  19. dp[i][j][1]=max(dp[i-1][j][1],dp[i-1][j-1][0])+a[i];
  20. }
  21. } int ans=0;
  22. for(int i=1;i<=w;i++)
  23. ans=max(ans,max(dp[n][i][1],dp[n][i][0]));
  24. printf("%d",ans); return 0;
  25. }

题目描述

作为在虚拟世界里统帅千军万马的领袖,小Z认为天时、地利、人和三者是缺一不可的,所以,谨慎地选择首都的位置对于小T来说是非常重要的。

首都被认为是一个占地C*C的正方形。小Z希望你寻找到一个合适的位置,使得首都所占领的位置的土地价值和最高。

输入输出格式

输入格式:

第1行:三个正整数N,M,C,表示地图的宽和长以及首都的边长。

第2∼N+1行:第i+1行包含M个整数,表示了地图上每个地块的价值。价值可能为负数。

输出格式:

一行,两个整数X、Y,表示首都左上角的坐标。

这个题其实可以用二维前缀和来做,也不算是DP了吧~。

首先是预处理:

picture1

我们设f[i][j]为以为右下角,为左上角的矩阵的所有点的点权和,那么我们可以知道这是个二维前缀和了,需要用点本身的价值+红色区域的矩形大小+灰色区域的矩形大小-棕色区域的矩阵大小(因为加了两遍),那么我们可以得出方程

处理完二维前缀和之后,接着一遍整个矩形,当然,横向的时候,我们只需要就可以,因为再下去也不可能的到边长为的矩形了,同理,竖向的时候,我们也只即可。这个时候我们枚举的是左上角的坐标,那么右下角的坐标就是

接下来就是取操作了,我们用来取,其实就是上一步的操作反过来而已。然后在更新的时候也记录一下就可以了。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define MAXN 1010
  6. #define INF 0x7fffffff
  7. using namespace std;
  8. int n,m,c,f[MAXN][MAXN];
  9. int a[MAXN][MAXN];
  10. int main(){
  11. scanf("%d%d%d",&n,&m,&c);
  12. for(int i=1;i<=n;i++)
  13. for(int j=1;j<=m;j++)
  14. scanf("%d",&a[i][j]);
  15. //输入
  16. for(int i=1;i<=n;i++)
  17. for(int j=1;j<=m;j++)
  18. f[i][j]=a[i][j]+f[i-1][j]+f[i][j-1]-f[i-1][j-1];
  19. //求二维前缀和
  20. int ans=-INF,mi,mj;
  21. for(int i=1;i<=n-c+1;i++)
  22. for(int j=1;j<=m-c+1;j++){
  23. int x=i+c-1; int y=j+c-1;
  24. if(ans<f[x][y]-f[i-1][y]-f[x][j-1]+f[i-1][j-1]){
  25. ans=f[x][y]-f[i-1][y]-f[x][j-1]+f[i-1][j-1];
  26. //取max操作
  27. mi=i; mj=j;
  28. }
  29. }
  30. printf("%d %d",mi,mj);
  31. return 0;
  32. }

题目背景

按售票处规定,每位购票者限购一张门票,且每张票售价为50元。在排成长龙的球迷中有N个人手持面值50元的钱币,另有N个人手持面值100元的钱币。假设售票处在开始售票时没有零钱。试问这2N个球迷有多少种排队方式可使售票处不致出现找不出钱的尴尬局面。

题目描述

例如当n=2是,用A表示手持50元面值的球迷,用B表示手持100元钱的球迷。则最多可以得到以下两组不同的排队方式,使售票员不至于找不出钱。

第一种:A A B B

第二种:A B A B

[编程任务]

对于给定的n (0≤n≤20),计算2N个球迷有多少种排队方式,可以使售票处不至于找不出钱。

输入输出格式

输入格式:

一个整数,代表N的值

输出格式:

一个整数,表示方案数

首先我们知道,购票处可以找的钱全部来自与之前收到的元钱,而每一个元都需要找钱,在这里是可以用卡特兰数来做的,但是毕竟是题解报告,我们运用一种的思路进行解题。

我们设为到了第个人,手里面现在有张可以找钱的,的到这种情况的方案数,那么考虑子问题,每一个都可以由上一个拿了一张的或者的转化而来,拿了了的话就是,(因为拿了就要找出出去,所以也要-1),如果拿了的就是,并且要注意是+=不是=。那么在这里我们不能单纯的取或者是,我们需要有一步判断,首先由于是个人,所以从1。j是从,并且还要有,因为前个人里面最多有。当,即可以找钱的时候,我们就由元转移而来,当的时候我们就由转移而来。最后输出即可.

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define MAXN 1010
  6. #define INF 0x7fffffff
  7. #define ll long long
  8. using namespace std;
  9. ll n,dp[MAXN][MAXN];
  10. int main(){
  11. scanf("%lld",&n);
  12. dp[0][0]=1;
  13. for(int i=1;i<=2*n;i++){
  14. for(int j=0;j<=n&&j<=i;j++){
  15. if(j>=1) dp[i][j]+=dp[i-1][j-1];
  16. if(j<=i) dp[i][j]+=dp[i-1][j+1];
  17. }
  18. }
  19. printf("%lld",dp[2*n][0]);
  20. return 0;
  21. }

题目描述

在一个的只包含0和1的矩阵里找出一个不包含的最大正方形,输出边长。

输入输出格式

输入格式:

输入文件第一行为两个整数,接下来行,每行个数字,用空格隔开,.

输出格式:

一个整数,最大正方形的边长

我们设为以为右下角,可以构成的最大正方形的边长,那么只有的时候,我们才将他左右正方形的右下角进行处理,那么对于一个已经确定的,如果,则表明向上个节点,向左个节点的正方形中所有的点都是,对于一个没有确定的,我们已知的值,那么我们可以得到状态转移方程:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define MAXN 110
  6. using namespace std;
  7. int n,m,f[MAXN][MAXN];
  8. bool map[MAXN][MAXN];
  9. int ans=0;
  10. int main(){
  11. scanf("%d%d",&n,&m);
  12. for(int i=1;i<=n;i++)
  13. for(int j=1;j<=m;j++){
  14. scanf("%d",&map[i][j]);
  15. if(map[i][j]==1)
  16. f[i][j]=min(min(f[i-1][j],f[i][j-1]),f[i-1][j-1])+1;
  17. ans=max(f[i][j],ans);
  18. }
  19. printf("%d",ans);
  20. return 0;
  21. }

约翰经常给产奶量高的奶牛发特殊津贴,于是很快奶牛们拥有了大笔不知该怎么花的钱.为此,约翰购置了份美味的零食来卖给奶牛们.每天约翰售出一份零食.当然约翰希望这些零食全部售出后能得到最大的收益.这些零食有以下这些有趣的特性:

零食按照..编号,它们被排成一列放在一个很长的盒子里.盒子的两端都有开口,约翰每天可以从盒子的任一端取出最外面的一个.

与美酒与好吃的奶酪相似,这些零食储存得越久就越好吃.当然,这样约翰就可以把它们卖出更高的价钱.

每份零食的初始价值不一定相同.约翰进货时,第份零食的初始价值为

第i份零食如果在被买进后的第天出售,则它的售价是

Vi的是从盒子顶端往下的第份零食的初始价值.约翰告诉了你所有零食的初始价值,并希望你能帮他计算一下,在这些零食全被卖出后,他最多能得到多少钱.

输入输出格式

输入格式:

Line 1: A single integer, N

Lines 2..N+1: Line i+1 contains the value of treat v(i)

输出格式:

Line 1: The maximum revenue FJ can achieve by selling the treats

这个题相比较来说就比较简单了,这个题的子问题就是上一个状态取了左端点还是右端点,我们设表示已取了个数,而左边取了个的最优解。(因为右边取了几个就是i-j个可以推。)设,左边取个的话,右边就是取个,那么就是,然后相反的就是,那么取一个就变成了:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define MAXN 2010
  6. using namespace std;
  7. int dp[MAXN][MAXN];
  8. int a[MAXN],n;
  9. int main(){
  10. scanf("%d",&n);
  11. for(int i=1;i<=n;i++) scanf("%d",&a[i]);
  12. for(int i=1;i<=n;i++)
  13. for(int j=0;j<=i;j++){
  14. int l=i-j;
  15. dp[i][j]=max(dp[i-1][j]+a[n-l+1]*i,dp[i-1][j-1]+a[j]*i);
  16. } int ans=0;
  17. for(int i=1;i<=n;i++) ans=max(ans,dp[n][i]);
  18. printf("%d",ans); return 0;
  19. }

题目描述

有N个不同的正整数数 排成一排,我们可以从左边或右边去掉连续的个数(只能从两边删除数),剩下个数,再把剩下的数按以上操作处理,直到所有的数都被删除为止。

每次操作都有一个操作价值,比如现在要删除从位置到位置上的所有的数。操作价值为,如果只去掉一个数,操作价值为这个数的值。 问如何操作可以得到最大值,求操作的最大价值。

输入输出格式

输入格式:

第一行为一个正整数

第二行有N个用空格隔开的个不同的正整数。

输出格式:

一行,包含一个正整数,为操作的最大值

这个题也是很简单了,我们设dp[i][j]为删除从i到j的数所能获得的最大价值,那么dp[i][i]很显然就是a[i],最终的答案就是dp[1][n],我们寻找子问题是什么:三重循环,我们先从1~n枚举i,然后从i~n枚举j,保证j一直在i后面,然后我们在i~j范围内枚举一个中间点k,那么dp[i][j]就可以由这个状态转移而来:首先删去i~k,然后再删除k+1~j,或者说直接保留dp[i][j],那么我们只需要去一个max就可以了。状态转移方程为:;

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define MAXN 10010
  6. using namespace std;
  7. int dp[MAXN][MAXN];
  8. int n,a[MAXN];
  9. int main(){
  10. scanf("%d",&n);
  11. for(int i=1;i<=n;i++){
  12. scanf("%d",&a[i]);
  13. dp[i][i]=a[i];
  14. }
  15. for(int i=1;i<=n;i++)
  16. for(int j=i+1;j<=n;j++)
  17. dp[i][j]=abs((a[j]-a[i])*(j-i+1));
  18. for(int i=1;i<=n;i++)
  19. for(int j=i;j<=n;j++)
  20. for(int k=i;k<j;k++){
  21. dp[i][j]=max(dp[i][k]+dp[k+1][j],dp[i][j]);
  22. }
  23. printf("%d",dp[1][n]); return 0;
  24. }

神魔之井的封印共有层,每层封印都有一个坚固值。身为魔族的龙溟单独打破一层封印时需要消耗的元气为该层封印的坚固值和封印总层数的平方的乘积; 但他运用越行术时,打破第层到第层封印的总元气消耗为第 层封印的坚固值之和与第层之间所有封印层(包括第层)的坚固值之和的乘积。同时,为了不惊动蜀山,第层封印的坚固值之和必须不大于一个固定值(单独打破时该层坚固值可以大于该值) 。

输入输出格式

输入格式:

第一行包含两个正整数,按序表示封印层数和题中所述的固定值。

第二行为个正整数,按序表示第层到第层封印的坚固值。

输出格式:

仅一行,包含一个正整数,表示最小消耗元气。

,我们先令表示打破前层,动态规划转移方程也很简单,就是;复杂度为,然后我们用前缀和的方式处理可以达到的复杂度

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define MAXN 10010
  6. #define ll long long
  7. using namespace std;
  8. ll a[MAXN],dp[MAXN];
  9. ll sum[MAXN],n,t;
  10. int main(){
  11. scanf("%lld%lld",&n,&t);
  12. for(int i=1;i<=n;i++){
  13. scanf("%lld",&a[i]);
  14. sum[i]=sum[i-1]+a[i];
  15. //前缀和
  16. }
  17. for(int i=1;i<=n;i++){
  18. dp[i]=dp[i-1]+a[i]*n*n;
  19. for(int j=1;j<=i-1;j++){
  20. ll k=a[j]+a[i];
  21. if(k<=t)
  22. dp[i]=min(dp[i],dp[j-1]+k*(sum[i]-sum[j-1]));
  23. }
  24. }
  25. printf("%lld",dp[n]); return 0;
  26. }

约翰家的头奶牛聚集在一起,排成一列,正在进行一项抗议活动。第头奶牛的理智度 为,可能是负数,约翰希望奶牛在抗议时保持理性,为此,他打算将所有的奶牛隔离成若干个小组,每个小组内的奶牛的理智度总和都要大于零。由于奶牛是按直线排列的,所以一个小组内的奶牛位置必须是连续的。请帮助约翰计算一下,最多分成几组。

输入输出格式

输入格式:

行包含个数,代表奶牛的数目。

至$$N+1$行每行$1$个整数$Ai$。

输出格式:

输出文件有且仅有一行,包含个整数即为最多组数。

若无法满足分组条件,则输出

我们设为前头牛所要分成的最少的组数。在这里我们也要用到前缀和,首先在读入的时候我们可以判断:如果的时候,就是说钱个牛放在一个小组里面时理智总和依然大于等于,那么就等于了,因为只要分一组就可以。那么截下来两重分别是,如果满足,即保证j到i这一组时,和是非负的,并且不为,则更新dp为:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define MAXN 10010
  6. #define ll long long
  7. using namespace std;
  8. ll n,a[MAXN],dp[MAXN];
  9. ll sum[MAXN];
  10. int main(){
  11. scanf("%lld",&n);
  12. for(int i=1;i<=n;i++){
  13. scanf("%lld",&a[i]);
  14. sum[i]=sum[i-1]+a[i];
  15. if(sum[i]>=0) dp[i]=1;
  16. }
  17. for(int i=1;i<=n;i++)
  18. for(int j=1;j<i;j++)
  19. if(sum[i]-sum[j]>=0&&dp[j])
  20. dp[i]=max(dp[j]+1,dp[i]);
  21. if(dp[n]) printf("%lld",dp[n]);
  22. else puts("Impossible");
  23. return 0;
  24. }

题目描述

上体育课的时候,小蛮的老师经常带着同学们一起做游戏。这次,老师带着同学们一起做传球游戏。

游戏规则是这样的:个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,每个同学可以把球传给自己左右的两个同学中的一个(左右任意),当老师再次吹哨子时,传球停止,此时,拿着球没有传出去的那个同学就是败者,要给大家表演一个节目。

聪明的小蛮提出一个有趣的问题:有多少种不同的传球方法可以使得从小蛮手里开始传的球,传了次以后,又回到小蛮手里。两种传球方法被视作不同的方法,当且仅当这两种方法中,接到球的同学按接球顺序组成的序列是不同的。比如有三个同学号、号、号,并假设小蛮为号,球传了次回到小蛮手里的方式有,共种。

输入输出格式

输入格式:

一行,有两个用空格隔开的整数

输出格式:

个整数,表示符合题意的方法数。

为经历了k次传球最后传到第i人手中的方案数。边界条件:经历次传到号手中的方案数为。我们知道第i个人手中的球只能从第和第个人手中得到,特殊的,第个人是从第个人和第个人手中得到的,第个人是从第个人和第个人手中得到的,我们特殊处理一下就好了。那么我们可以知道球经历k次传到第个人手中的方案数就是球经历次传到第个人的方案数加上球经历次传到第个人手中的方案数,那么可以得到状态转移方程:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define MAXN 50
  6. using namespace std;
  7. int n,m,dp[MAXN][MAXN];
  8. int main(){
  9. scanf("%d%d",&n,&m);
  10. dp[1][0]=1;
  11. for(int k=1;k<=m;k++){
  12. dp[1][k]=dp[2][k-1]+dp[n][k-1];
  13. for(int i=2;i<n;i++)
  14. dp[i][k]=dp[i-1][k-1]+dp[i+1][k-1];
  15. dp[n][k]=dp[n-1][k-1]+dp[1][k-1];
  16. }
  17. printf("%d",dp[1][m]); return 0;
  18. }

题目描述

小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共盆。通过调查顾客的喜好,小明列出了顾客最喜欢的种花,从标号。为了在门口展出更多种花,规定第种花不能超过盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。

试编程计算,一共有多少种不同的摆花方案。

输入输出格式

输入格式:

第一行包含两个正整数,中间用一个空格隔开。

第二行有个整数,每两个整数之间用一个空格隔开,依次表示

输出格式:

一个整数,表示有多少种方案。注意:因为方案数可能很多,请输出方案数对取模的结果。

一看就是一道区间,我们设为摆了种花,总共摆了盆的方案总数。初始化的时候我们想,无论有多少种花,如果盆数为,那么方案数肯定是了,所以;循环一共是三重,最外面自然是枚举所有的种数,然后在里面一个,从开始一直到,表示盆第种花,我们知道的转移必定要由前种花的方案数有关,那么我们继续在里面枚举一个,用来枚举这种花的数量,状态转移方程就是:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define MAXN 500
  6. #define rqy 1000007
  7. using namespace std;
  8. int n,m,a[MAXN];
  9. int dp[MAXN][MAXN];
  10. int main(){
  11. scanf("%d%d",&n,&m);
  12. for(int i=1;i<=n;i++)
  13. scanf("%d",&a[i]);
  14. for(int i=0;i<=n;i++)
  15. dp[i][0]=1;
  16. for(int i=1;i<=n;i++){
  17. for(int j=0;j<=a[i];j++){
  18. for(int k=0;k<=m-j;k++){
  19. if(j==0&&k==0) continue;
  20. dp[i][j+k]+=dp[i-1][k];
  21. dp[i][j+k]%=rqy;
  22. }
  23. }
  24. }
  25. printf("%d",dp[n][m]%rqy);
  26. return 0;
  27. }

 题目描述

乌龟棋的棋盘是一行NNN个格子,每个格子上一个分数(非负整数)。棋盘第1格是唯一的起点,第NNN格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。

乌龟棋中MMM张爬行卡片,分成4种不同的类型(MMM张卡片中不一定包含所有444种类型的卡片,见样例),每种类型的卡片上分别标有1,2,3,41,2,3,41,2,3,4四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。

游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。

很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。

现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?

输入输出格式

输入格式:

每行中两个数之间用一个空格隔开。

个正整数分别表示棋盘格子数和爬行卡片数。

个非负整数,,其中​表示棋盘第个格子上的分数。

个整数,,表示M张爬行卡片上的数字。

输入数据保证到达终点时刚好用光张爬行卡片。

输出格式:

个整数,表示小明最多能得到的分数。

题目既然是说了有不同的选择卡片的方法和顺序,那么也就表明选择卡片的方法和顺序是得到分数不同的决定性因素,那么我们考虑对这个卡片的数目进行Dp。

在这里我们设表示第一种卡片选了个,第二种卡片选了个,第三种卡片选了个,第四种卡片选了个能够得到的最大的分数。

对于我们当前摸到的(循环到的)每一张卡片,选择无非有两种:或者是不选,不选的话,我么那就加上当前应该到的各自的值。关于这个格子的计算:

假设说我们当前卡片各获得了个,那么很显然现在到的格子就是 * * * ;

而因为乌龟棋是从1号节点开始的,所以最后要加上一个。那么我们得到了状态转移方程式:

当然,这个前提是:每一个对应的里的都不等于才行。那么当然里面的max函数就只有两个参数,所以自然要分成来取。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. #define MAXN 350
  6. #define MAXM 120
  7. #define Inf 0x7fffffff
  8. #define LL long long
  9. using namespace std ;
  10. int N, M, Value[MAXN], Card[5] ;
  11. int Dp[MAXM][MAXM][MAXM][MAXM] ;
  12. int Max_Num ;
  13. int Read(){
  14. int X = 0 ; char ch = getchar() ;
  15. while(ch > '9' || ch < '0') ch = getchar() ;
  16. while(ch <= '9' && ch >= '0')
  17. X = (X << 1) + (X << 3) + (ch ^ 48), ch = getchar() ;
  18. return X ;
  19. }
  20. int main(){
  21. N = Read() ; M = Read() ;
  22. for(int i = 1; i <= N; i ++){
  23. Value[i] = Read() ;
  24. }
  25. Dp[0][0][0][0] = Value[1] ;
  26. for(int i = 1; i <= M; i ++){
  27. int X ; X = Read() ;
  28. Card[X] ++ ;
  29. }
  30. for(int A = 0; A <= Card[1]; A ++)
  31. for(int B = 0; B <= Card[2]; B ++)
  32. for(int C = 0; C <= Card[3]; C ++)
  33. for(int D = 0; D <= Card[4]; D ++){
  34. int R = A + B * 2 + C * 3 + D * 4 + 1 ;
  35. if(A != 0) Dp[A][B][C][D] = max(Dp[A][B][C][D], Dp[A - 1][B][C][D] + Value[R]) ;
  36. if(B != 0) Dp[A][B][C][D] = max(Dp[A][B][C][D], Dp[A][B - 1][C][D] + Value[R]) ;
  37. if(C != 0) Dp[A][B][C][D] = max(Dp[A][B][C][D], Dp[A][B][C - 1][D] + Value[R]) ;
  38. if(D != 0) Dp[A][B][C][D] = max(Dp[A][B][C][D], Dp[A][B][C][D - 1] + Value[R]) ;
  39. }
  40. printf("%d", Dp[Card[1]][Card[2]][Card[3]][Card[4]]) ;
  41. return 0 ;
  42. }

那么暂时到此为止(个题还没刷完啊啊啊啊啊)

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注