SDAU训练日志第16篇----------动态规划(12)(2018年2月22日)

本来想五号就开始重新写来着,奈何过完年又得了重感冒,拖到了现在。

把过完年之后A掉的几个题放上来吧。

(洛谷P1006)题目描述

小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排做成一个m行n列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n)。从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。
   在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙。反之亦然。
还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用0表示),可以用一个0-100的自然数来表示,数越大表示越好心。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度只和最大。现在,请你帮助小渊和小轩找到这样的两条路径。

输入输出格式

输入格式:

  输入文件message.in的第一行有2个用空格隔开的整数m和n,表示班里有m行n列(1<=m,n<=50)。接下来的m行是一个m*n的矩阵,矩阵中第i行j列的整数表示坐在第i行j列的学生的好心程度。每行的n个整数之间用空格隔开。
输出格式:
输出文件message.out共一行,包含一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。
输入输出样例
输入样例#1: 
3 3
0 3 9
2 8 5
5 7 0
输出样例#1: 
34
说明
【限制】
30%的数据满足:1<=m,n<=10

100%的数据满足:1<=m,n<=50

关键思路:

1.所谓A->B 然后B->A其实就是A->B走两次,这样可以简化问题。

2.建立一个四维DP数组,dp[a][b][c][d]表示第一次从a走到b,第二次从c走到d

3.不能走重复路线这个可以有两种思路,一种是走一次之后把好心度清0或者设成负无穷(好像不太好搞),第二种是通过if语句判断是否是同一个点过来的,如果判定成功不加第二条路线的好心度,这样的话重复的好心度就不会被算进去。即

 if(i!=h&&j!=k) dp[i][j][h][k]+=a[h][k];
4.应该考虑四种情况取最优,即:

int temp1=max(dp[i-1][j][h-1][k],dp[i][j-1][h][k-1]);
int temp2=max(dp[i-1][j][h][k-1],dp[i][j-1][h-1][k]);
dp[i][j][h][k]=max(temp1,temp2)+a[i][j];

上代码:

#include<iostream>
using namespace std;
int a[52][52];//存放好心程度
int dp[52][52][52][52];
//该问题可以看成从A->B走两次,dp[a][b][c][d],
int main()
{
    int m=0,n=0;
    cin>>m>>n;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            cin>>a[i][j];
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            for(int h=1;h<=m;h++)
                for(int k=1;k<=n;k++)
                {
                    int temp1=max(dp[i-1][j][h-1][k],dp[i][j-1][h][k-1]);
                    int temp2=max(dp[i-1][j][h][k-1],dp[i][j-1][h-1][k]);
                    dp[i][j][h][k]=max(temp1,temp2)+a[i][j];
                    if(i!=h&&j!=k) dp[i][j][h][k]+=a[h][k];
                }
        cout<<dp[m][n][m][n]<<endl;
}


(洛谷P1541)题目描述
     乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数)。棋盘第1格是唯一的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。
     乌龟棋中M张爬行卡片,分成4种不同的类型(M张卡片中不一定包含所有4种类型的卡片,见样例),每种类型的卡片上分别标有1、2、3、4四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。
     游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。
     很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。
     现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?
输入输出格式
输入格式:
     输入文件的每行中两个数之间用一个空格隔开。
     第1行2个正整数N和M,分别表示棋盘格子数和爬行卡片数。
     第2行N个非负整数,a1a2……aN,其中ai表示棋盘第i个格子上的分数。
     第3行M个整数,b1b2……bM,表示M张爬行卡片上的数字。
     输入数据保证到达终点时刚好用光M张爬行卡片。
输出格式:
     输出只有1行,1个整数,表示小明最多能得到的分数。
输入输出样例
输入样例#1: 复制
9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1
输出样例#1: 复制
73
说明
小明使用爬行卡片顺序为1,1,3,1,2,得到的分数为6+10+14+8+18+17=73。注意,由于起点是1,所以自动获得第1格的分数6。
对于30%的数据有1≤N≤30,1≤M≤12。
对于50%的数据有1≤N≤120,1≤M≤50,且4种爬行卡片,每种卡片的张数不会超过20。

对于100%的数据有1≤N≤350,1≤M≤120,且4种爬行卡片,每种卡片的张数不会超过40;0≤ai≤100,1≤i≤N;1≤bi≤4,1≤i≤M。

关键思路

1.这题是个多重背包的动态规划题。

各个物品可以视为1,价值为王八位置的分数,即val[long_1+long_2*2+long_3*3+long_4*4+1]//+1是因为从第一个格子出发的。

2.分别求出取四种卡片时的最优解,进行比较

3.一定要注意边界(经常死在边界上的我),没有卡的时候就不要进行dp运算了,否则会使DP数组下标变成-1,导致RE错误。

4.好像也没啥要特别说的了,那就上代码吧。

上代码:

#include<iostream>
using namespace std;
int dp[42][42][42][42];
int i,n,m,b[5],a[400],k;
int long_1,long_2,long_3,long_4;  
int main()  
{
    cin>>n>>m;
    for (i=1;i<=n;i++)  
      cin>>a[i];
    for (i=1;i<=m;i++)  
     {
        cin>>k;
         b[k]++;  
    }   
    int t;  
    dp[0][0][0][0]=a[1];  
    for (long_1=0;long_1<=b[1];long_1++)  
       for (long_2=0;long_2<=b[2];long_2++)  
          for (long_3=0;long_3<=b[3];long_3++)  
             for (long_4=0;long_4<=b[4];long_4++)  
            {
                t=long_1+long_2*2+long_3*3+long_4*4+1;  
                if(long_1!=0) dp[long_1][long_2][long_3][long_4]=max(dp[long_1][long_2][long_3][long_4],dp[long_1-1][long_2][long_3][long_4]+a[t]);  
                if(long_2!=0) dp[long_1][long_2][long_3][long_4]=max(dp[long_1][long_2][long_3][long_4],dp[long_1][long_2-1][long_3][long_4]+a[t]);  
                if(long_3!=0) dp[long_1][long_2][long_3][long_4]=max(dp[long_1][long_2][long_3][long_4],dp[long_1][long_2][long_3-1][long_4]+a[t]);  
                if(long_4!=0) dp[long_1][long_2][long_3][long_4]=max(dp[long_1][long_2][long_3][long_4],dp[long_1][long_2][long_3][long_4-1]+a[t]);  
            }  
    cout<<dp[b[1]][b[2]][b[3]][b[4]]<<endl;    
}//参考了题解的思路做的

剩下的几个题明天再放吧。

感冒还没好。。。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值