P1541 乌龟棋

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

说明

每个测试点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。

洛谷题解:

超级暴力的四次方DP。

我们每次的决策是选择爬行卡片来决定乌龟棋的行动,那么好,我们用一个f数组记录使用i张1点数卡,j张2点数卡,k张3点数卡,l张4点数卡所能拿到的最多分数。

那么方程是f[i][j][k][l] =max(f[ i-1 ][ j ][ k ][ l ],f[ i ][ j-1 ][ k ][ l ],f[ i ][ j ][ k-1 ][ l ],f[ i ][ j ][ k ][ l-1 ]) + a[1 + i + j*2 + k*3 + l*4];//为了看起来方便

我们开一个数组记录每个点数的牌一共有多少张,然后从0到这个数枚举,进行转移就好。

这题就是把位置确定,方便取数。

而DP的实质就是妥妥记录了之前状态的枚举

从以下两点就可以看出动态转移方程了:

1.一看就4种牌,而且每一种牌都不多于40张。

2.到了终点每种牌都会被用完。

所以方程就出来了:f[i,j,k,l]:=max{f[i-1,j,k,l]f[i,j-1,k,l],f[i,j,k-1,l],f[i,j,k,l-1]}+a[x];

这里的i,j,k,l分别表示用了i张第一种牌,j张第二种牌,k张第三种牌,l张第四种牌,x表示当前在第x格(自然是在i+j*2+k*3+l*4+1处)。

这里要说明一下要在读入时就记录每一种牌的数量,之后就可以开始枚举了。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5     int m,n,f[42][42][42][42],c1,c2,c3,c4,a[351],b[351],maxn;
 6 int main()
 7 {
 8     scanf("%d%d",&n,&m);
 9     c1=0;//c1c2c3c4存数量
10     c2=0;
11     c3=0;
12     c4=0;
13     for (int i=1;i<=n;i++)    
14     scanf("%d",&a[i]);//输入值
15 for (int j=1;j<=m;j++)
16 {
17     scanf("%d",&b[j]);
18     if (b[j]==1) c1++;//加数量
19     if (b[j]==2) c2++;
20     if (b[j]==3) c3++;
21     if (b[j]==4) c4++;
22 }
23 f[0][0][0][0]=a[1];//赋初值,一开始赋为第一块
24 for (int i=0;i<=c1;i++)//开始循环枚举,用哪张
25     for (int j=0;j<=c2;j++)
26       for (int k=0;k<=c3;k++)
27         for (int l=0;l<=c4;l++)
28              {
29               maxn=0;
30         if (i)    maxn=max(f[i-1][j][k][l],maxn);//动归转移方程
31     if (j)    maxn=max(f[i][j-1][k][l],maxn);
32         if (k)    maxn=max(f[i][j][k-1][l],maxn);
33         if  (l)    maxn=max(f[i][j][k][l-1],maxn);
34 // //以上四行是由上阶段开始枚举每一张牌来选出目标阶段的最优解      
35     f[i][j][k][l]=maxn+a[i+j*2+k*3+l*4+1];
36               }
37 printf("%d",f[c1][c2][c3][c4]);//最终值就是全用完
38 return 0;
39 }
原文地址:https://www.cnblogs.com/Renyi-Fan/p/7725785.html