洛谷题目链接:乌龟棋
题目背景
小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。
题目描述
乌龟棋的棋盘是一行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
说明
每个测试点1s
小明使用爬行卡片顺序为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。
一句话题意: 给出一个长度为\(n\)的序列,以及\(m\)张卡片,分别可以让乌龟前进1,2,3,4个格子.到一个格子就可以获得该格子的价值(数据保证最终会到\(n\)位置),求最大可以获得的价值.
题解: 首先贪心是肯定有问题的,贪心只能获得当前局面的最优解.所以我们可以考虑DP.
只有这些东西对我们枚举的状态有影响:距离以及各种牌的数量.所以可以定义一个状态\(f[i][j][k][l][o]\)表示到\(i\)位置,还剩下\(j\)张1号牌,\(k\)张2号牌,\(l\)张3号牌,\(o\)张4号牌.然后5重循环枚举距离以及四种牌还剩下的数量.
然而这样还不能A掉这题.
我们需要优化掉一维.一开始我想通过已经用过的三种牌来算第4种牌的数量,但是发现这样不能存下状态.然后发现距离是可以通过已经用了的牌的数量算出来的,所以可以优化掉\([i]\)那一维,定义状态f[j][k][l][o]表示剩余四种牌的数量,然后直接枚举四种牌剩余数量,来算出这个距离.
#include<bits/stdc++.h>
using namespace std;
const int N=350+5;
const int M=40+5;
const int inf=2147483647;
int n, m, w[N], a[120+5], b[5];
//int f[N][M][M][M][M];
int f[M][M][M][M];
//the remaining 1st,2nd,3rd,4th cards
int getmax();
void print();
int dist(int j,int k,int l,int o){
return 1+(b[1]-j)+(b[2]-k)*2+(b[3]-l)*3+(b[4]-o)*4;
}
int main(){
//freopen("data.in","r",stdin);
cin >> n >> m;
for(int i=1;i<=n;i++) cin >> w[i];
for(int i=1;i<=m;i++) cin >> a[i], b[a[i]]++;
memset(f,128,sizeof(f)); f[b[1]][b[2]][b[3]][b[4]] = w[1];
for(int j=b[1];j>=0;j--)
for(int k=b[2];k>=0;k--)
for(int l=b[3];l>=0;l--)
for(int o=b[4];o>=0;o--){
int dis = dist(j,k,l,o);
if(dis>=2 && j<b[1]) f[j][k][l][o] = max(f[j][k][l][o],f[j+1][k][l][o]+w[dis]);
if(dis>=3 && k<b[2]) f[j][k][l][o] = max(f[j][k][l][o],f[j][k+1][l][o]+w[dis]);
if(dis>=4 && l<b[3]) f[j][k][l][o] = max(f[j][k][l][o],f[j][k][l+1][o]+w[dis]);
if(dis>=5 && o<b[4]) f[j][k][l][o] = max(f[j][k][l][o],f[j][k][l][o+1]+w[dis]);
//if(f[j][k][l][o] > 0) printf("f[%d][%d][%d][%d]=%d\n",j,k,l,o,f[j][k][l][o]);
}
printf("%d\n",f[0][0][0][0]);//最后一张牌都不剩的情况就是到n位置的情况
return 0;
}