题目背景
小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。
题目描述
乌龟棋的棋盘是一行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个整数,表示小明最多能得到的分数。
输入输出样例
输入 #1复制
9 5 6 10 14 2 8 8 18 5 17 1 3 1 2 1
输出 #1复制
73
说明/提示
每个测试点1s1s
小明使用爬行卡片顺序为1,1,3,1,21,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≤bi≤4,1≤i≤M。
最开始自己状态定义错了,反思一下:
问了下抽风:为什么错
这么定义不满足最优子结构
比如: 比如对于这个样例来说,
走到 5 的位置(第二个8),
能拿到的最大的分数路线为:1 1 2 1, 能够获得的分数为46,
但是,在结果中,走的路线为 1 1 3 1 2,
虽然也走到了 5 的位置,但并不包含到 5 的位置最优的路线, 然后再转移,自然就会出错
滑稽dalao:
你走到某一步,剩下一张4号牌,和剩下一张1号牌,情况是不一样的,你必须要记录这个情况
而且牌不是可以随便用嘛,不按顺序
最后我自己理解如下:
对于到达的每一个点,有可能从1步,2步,3步,4步转移过来(有点像走楼梯的dp)
(用dfs理解理解的话) 因此需要统计用了多少的1步,2步,3步,4步的卡牌的个数,记录当前到这个点是各自用了多少张
用dp理解的话:这一点由前1,2,3,4的状态转移过来.对于每个卡都有上限,类似多重背包
而开始dp错误的原因是:只从第j张牌转移过来,j从1到最后,实际上是按照了输入牌的顺序转移的,而牌实际上是随机的顺序,因此多重背包来解决
刚开始想错的:
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=500;
typedef long long LL;
LL a[maxn],b[maxn];
LL f[maxn][maxn];// f[i,j]:到第i个点使用前j张牌的最多分数
int main(void)
{
cin.tie(0);std::ios::sync_with_stdio(false);
LL n,m;cin>>n>>m;
for(LL i=1;i<=n;i++) cin>>a[i];
for(LL i=1;i<=m;i++) cin>>b[i];
f[1][0]=a[1];
for(LL i=1;i<=n;i++)
for(LL j=1;j<=m;j++)
f[i][j]=max(f[i][j-1],f[i-b[j]][j-1]+a[i]);
cout<<f[n][m]<<endl;
return 0;
}
改正:
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=360;
typedef long long LL;
LL a[maxn],b[130];
LL f[125][125][125][125];
//用了i张1,j张2,k张3,p张4达到的最大值
int main(void)
{
cin.tie(0);std::ios::sync_with_stdio(false);
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++)
{
LL x;cin>>x;
b[x]++;
}
f[0][0][0][0]=a[1];
for(int i=0;i<=b[1];i++)
for(int j=0;j<=b[2];j++)
for(int k=0;k<=b[3];k++)
for(int p=0;p<=b[4];p++)
{
LL t=a[i+j*2+k*3+p*4+1];//1+卡牌的长度==到的点的下标
if(i) f[i][j][k][p]=max(f[i][j][k][p],f[i-1][j][k][p]+t);
if(j) f[i][j][k][p]=max(f[i][j][k][p],f[i][j-1][k][p]+t);
if(k) f[i][j][k][p]=max(f[i][j][k][p],f[i][j][k-1][p]+t);
if(p) f[i][j][k][p]=max(f[i][j][k][p],f[i][j][k][p-1]+t);
}
cout<<f[b[1]][b[2]][b[3]][b[4]]<<endl;
return 0;
}