CJOJ 1087 【NOIP2010】乌龟棋 / Luogu 1541 乌龟棋(动态规划)
Description
小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。
乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数)。棋盘第1格是唯一的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。
乌龟棋中M张爬行卡片,分成4种不同的类型(M张卡片中不一定包含所有4种类型的卡片,见样例),每种类型的卡片上分别标有1、2、3、4四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。
游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。
很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。
现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?
Input
每行中两个数之间用一个空格隔开。
第1行2个正整数N和M,分别表示棋盘格子数和爬行卡片数。
第2行_N个非负整数,a1, a2,……, aN,其中ai表示棋盘第i个格子上的分数。
第3行M个整数,b1,b2,……, bM,表示M张爬行卡片上的数字。
输入数据保证到达终点时刚好用光M张爬行卡片,即N−1=ΣM (ΣM表示M张卡片数字的和)
Output
输出只有1行,1个整数,表示小明最多能得到的分数。
Sample Input
9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1
Sample Output
73
Http
CJOJ:http://oj.changjun.com.cn/problem/detail/pid/1087
Luogu:https://www.luogu.org/problem/show?pid=1541
Source
动态规划
解决思路
我们首先想到的是令F[x][a][b][c][d]表示走到x格时用了a格爬1格,b个爬2格,c个爬3格,d个爬4个的卡片所能有的最大得分。那么我们就能得到一个初始的动态转移方程
\begin{cases}
F[x-1][a-1][b][c][d]& \text{a!=0}\\F[x-2][a][b-1][c][d]& \text{b!=0} \\ F[x-3][a][b][c-1][d]& \text{c!=0} \\ F[x-4][a][b][c][d-1]& \text{d!=0}\end{cases} +Value[x]\]
但是这样是通不过空间限制的。题中已经给出了用这M张卡片一定能到达n格,所以我们可以省掉x那一维数组,直接用a,b,c,d推出x,所以动态转移方程为
\begin{cases}
F[a-1][b][c][d]& \text{a!=0}\\F[a][b-1][c][d]& \text{b!=0} \\ F[a][b][c-1][d]& \text{c!=0} \\ F[a][b][c][d-1]& \text{d!=0}\end{cases} +Value[a+b*2+c*3+d*4+1]\]
注意最后要+1,因为起始格是第一格。
为了简便,我们还是使用记忆化的方法。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxN=351;
const int maxC=41;
const int inf=2147483647;
int n,m;
int Card[10]={0};
int Value[maxN];
int F[maxC][maxC][maxC][maxC];
int dfs(int a,int b,int c,int d);
int main()
{
int x;
cin>>n>>m;
for (int i=1;i<=n;i++)
cin>>Value[i];
for (int i=1;i<=m;i++)
{
cin>>x;
Card[x]++;//直接累加到同类卡片的计数器中
}
memset(F,-1,sizeof(F));
F[0][0][0][0]=Value[1];
F[Card[1]][Card[2]][Card[3]][Card[4]]=dfs(Card[1],Card[2],Card[3],Card[4]);
cout<<F[Card[1]][Card[2]][Card[3]][Card[4]]<<endl;
return 0;
}
int dfs(int a,int b,int c,int d)//记忆化搜索
{
if (F[a][b][c][d]!=-1)
return F[a][b][c][d];
int k=a+b*2+c*3+d*4+1;
if (a!=0)
F[a][b][c][d]=max(F[a][b][c][d],dfs(a-1,b,c,d));
if (b!=0)
F[a][b][c][d]=max(F[a][b][c][d],dfs(a,b-1,c,d));
if (c!=0)
F[a][b][c][d]=max(F[a][b][c][d],dfs(a,b,c-1,d));
if (d!=0)
F[a][b][c][d]=max(F[a][b][c][d],dfs(a,b,c,d-1));
F[a][b][c][d]+=Value[k];
return F[a][b][c][d];
}