题目背景

小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。

题目描述

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

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

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

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

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

输入输出格式

输入格式:

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

第 1 行 2 个正整数 N,M,分别表示棋盘格子数和爬行卡片数。

第 2 行 N 个非负整数, a_1,a_2,…,a_N,其中 a_i 表示棋盘第 i个格子上的分数。

第 3 行 M 个整数, b_1,b_2,…,b_Mb表示M张爬行卡片上的数字。

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

输出格式:

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

输入输出样例

输入样例#1:

9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1
输出样例#1:

73

Luogu P1541 乌龟棋 【线性dp】-LMLPHP

在讲序列dp的时候讲到了这道题...一看最开始竟然想到了向量(什么?)

lyd(林永迪)说一般做在序列上的dp都以下标为阶段划分,然后就说了这题。

结果我就傻乎乎地设计为f[i]为到达i所得到的最大价值233.

转念让我们再想想,真的是这样嘛?那出牌的顺序怎么搞??

然而...正解是把出牌当做阶段划分Orz。

设f[a][b][i][j]为第一张牌出了a个,第二张牌出了b个,第三张牌出了i个,第四张牌出了j个的最大价值。那么我们还可以确定当前点所在序列上的位置--a+2*b+3*i+4*j
真的是这样嘛?

再一次被lyd骗了...

我们是从1出发的,所以真位置为a+2*b+3*i+4*j+1啊!

转移就很好想了。可以由4个牌分别转移过来,但是注意RE问题,因为选牌的个数可以为0,所以在进行减一转移时,要判断是否满足下标不会变为负数。

初值嘛,只设f[0][0][0][0]==seq[1]就好了。

转移还有一个细节,每次转移都要带着当前点的权值,而不是最后再加。因为不加当前点的时候可能比不过曾经,转移也就没有效果。

因为数据保证所有牌都用完,所以我们的目标状态就是都装满时的状态。

code

 #include<cstdio>
#include<algorithm> using namespace std; int n,m,x;
int a,b,c,d,ans;
int seq[];
int f[][][][]; int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++) scanf("%d",&seq[i]);
for(int i=;i<=m;i++)
{
scanf("%d",&x);
if(x==) a++;
else if(x==) b++;
else if(x==) c++;
else if(x==) d++;
}
f[][][][]=seq[];
for(int i=;i<=a;i++)
for(int j=;j<=b;j++)
for(int l=;l<=c;l++)
for(int r=;r<=d;r++)
{
int s=+i+j*+l*+r*;
if(i->=) f[i][j][l][r]=max(f[i][j][l][r],f[i-][j][l][r]+seq[s]);
if(j->=) f[i][j][l][r]=max(f[i][j][l][r],f[i][j-][l][r]+seq[s]);
if(l->=) f[i][j][l][r]=max(f[i][j][l][r],f[i][j][l-][r]+seq[s]);
if(r->=) f[i][j][l][r]=max(f[i][j][l][r],f[i][j][l][r-]+seq[s]);
}
printf("%d",f[a][b][c][d]);
return ;
}
05-12 16:16