dp——洛谷 P1541 乌龟棋 —— by hyl天梦
hyl天梦 人气:1题目:(转自 https://www.luogu.com.cn/problem/P1541)
题目描述
乌龟棋的棋盘是一行NN个格子,每个格子上一个分数(非负整数)。棋盘第1格是唯一的起点,第NN格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。
乌龟棋中MM张爬行卡片,分成4种不同的类型(MM张卡片中不一定包含所有44种类型的卡片,见样例),每种类型的卡片上分别标有1,2,3,41,2,3,4四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。
游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。
很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。
现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?
输入格式
每行中两个数之间用一个空格隔开。
第11行22个正整数N,MN,M,分别表示棋盘格子数和爬行卡片数。
第22行NN个非负整数,a_1,a_2,…,a_Na1,a2,…,aN,其中a_iai表示棋盘第ii个格子上的分数。
第33行MM个整数,b_1,b_2,…,b_Mb1,b2,…,bM,表示M张爬行卡片上的数字。
输入数据保证到达终点时刚好用光MM张爬行卡片。
输出格式
11个整数,表示小明最多能得到的分数。
输入输出样例
9 5 6 10 14 2 8 8 18 5 17 1 3 1 2 1
73
说明/提示
每个测试点1s1s
小明使用爬行卡片顺序为1,1,3,1,2
1,1,3,1,2,得到的分数为6+10+14+8+18+17=736+10+14+8+18+17=73。注意,由于起点是11,所以自动获得第11格的分数66。
对于30\%30%的数据有1≤N≤30,1≤M≤121≤N≤30,1≤M≤12。
对于50\%50%的数据有1≤N≤120,1≤M≤501≤N≤120,1≤M≤50,且44种爬行卡片,每种卡片的张数不会超过2020。
对于100\%100%的数据有1≤N≤350,1≤M≤1201≤N≤350,1≤M≤120,且44种爬行卡片,每种卡片的张数不会超过4040;0≤a[i]≤100,1≤i≤N,1≤b[i]≤4,1≤i≤M0≤ai≤100,1≤i≤N,1≤b[i]≤4,1≤i≤M。
很明显,暴力dfs是一个不错的选择,如若是在考试,天梦在这里奉告读者结合实际情况打暴力。但由于是一道dp,我们今天就讲讲正解。
思路:
在做这道题时,我首先这样想:把起始点标注成s和t,在中间找一点i保证 f[s][i] 和 f[i][t] 最优,通过 f[i][j]=f[s][i]+f[i][t]这个式子来让f[s][t]最优,但可以很快证明这个想法是错误的,因为设我们能用的总牌数为K,
前一段(即f[s][i])为了得到最优解会用a张牌,后一段(即f[i][t])为了得到最优解会用b张牌,无法保证a+b=K。
除了枚举分段点,我们还能枚举什么? 用的牌数。接下来我来给大家做简单介绍。
介绍:
设f[i][j][p][k]表示用了i张走一步的牌(下面简写),j张2,p张3,k张4,则我们理想的最优解应该是他走到终点时,即所有牌都用完时。我们以样例为例:f【3】【1】【1】【0】为我们所求的最优解,因为上一步可能走了1格,2格,3格,4格(由于题目限制,4在样例中牌数为0)
那么最优解可以由f[2][1][1][0],f[3][0][1][0],f[3][1][0][0] 得到
一般的f[i][j][k][p]可以由f[i-1][j][k][p], f[i][j-1][k][p], f[i][j][k-1][p], f[i][j][k][p-1]得到,如果读者接着往上推(试试推推样例) 会发现最终的最优解都是由f[0][0][0][0]得到的,它的值是什么?一格都没走——起点值。
下面,让我们来看看代码:
代码:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<queue> #include<vector> #define N 400 #define ll long long using namespace std; int n,m; int a[N]; int sum[5]; int f[41][41][41][41]; int main() { cin>>n>>m; for(int i=1;i<=n;i++) { cin>>a[i]; } for(int i=1;i<=m;i++) { int x; cin>>x; sum[x]++; }//计算牌数 //memset(f,0,sizeof(f)); f[0][0][0][0]=a[1]; //cout<<sum[1]<<" "<<sum[2]<<" "<<sum[3]<<" "<<sum[4]<<endl<<endl; for(int i=0;i<=sum[1];i++) { for(int j=0;j<=sum[2];j++) { for(int k=0;k<=sum[3];k++) { for(int p=0;p<=sum[4];p++) { if(!(i==0&&j==0&&k==0&&p==0)) { int ans1,ans2,ans3,ans4; if(i-1<0) ans1=0; else ans1=f[i-1][j][k][p]; if(j-1<0) ans2=0; else ans2=f[i][j-1][k][p]; if(k-1<0) ans3=0; else ans3=f[i][j][k-1][p]; if(p-1<0) ans4=0; else ans4=f[i][j][k][p-1];//防止数组下溢 f[i][j][k][p]=max(max(ans1,ans2),max(ans3,ans4))+a[i*1+j*2+k*3+p*4+1];//加上这个位置的格子值,别忘了加1 } } } } } cout<<f[sum[1]][sum[2]][sum[3]][sum[4]]; return 0; }
加载全部内容