乌龟棋Noip2010

 

[Noip2010]乌龟棋

一道DP水题

题目描述

小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。乌龟棋的棋盘是一行N 个格子,每个格子上一个分数(非负整数)。棋盘第1 格是唯一的起点,第N 格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。



乌龟棋Noip2010_第1张图片




乌龟棋中M 张爬行卡片,分成4 种不同的类型(M 张卡片中不一定包含所有4 种类型的卡片,见样例),每种类型的卡片上分别标有1、2、3、4 四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?
输入
输入文件tortoise.in


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


第1 行2 个正整数N 和M,分别表示棋盘格子数和爬行卡片数。
第2 行N 个非负整数,a1, a2, ……, aN,其中ai 表示棋盘第i 个格子上的分数。
第3 行M 个整数,b1,b2, ……, bM,表示M 张爬行卡片上的数字。
输入数据保证到达终点时刚好用光M 张爬行卡片,N-1=Sum(bi){1<=i<=M}


输出
tortoise.out


输出只有1 行,1 个整数,表示小明最多能得到的分数.


样例输入
9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1


13 8
4 96 10 64 55 13 94 53 5 24 89 8 30
1 1 1 1 1 2 4 1
样例输出
73


455
提示
【输入输出样例 1 说明】


小明使用爬行卡片顺序为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。输入数据保证sigma(bi)=N-1;

 

 

题解+分析

 

某dalao给的好思路 
一看题,dalao说开4维,我当即想到用5维表示每个状态来DP
dp[i][j][k][l][t]->第i个位置,每种牌剩j k l t个 
开出来就发现5维根本RE

 

以下敬上最朴素代码(根本没法运行的) 
 

#include
using namespace std;
int n,m,a[360],b[5];
//错误代码,数组开不下 
int dp[350][42][42][42][42];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	int scan;
	for(int i=1;i<=m;i++){
		cin>>scan;
		b[scan]++;
	}
	dp[1][b[1]][b[2]][b[3]][b[4]]=a[i];
	for(int i=1;i<=n;i++)
		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 t=b[4];t>=0;t--)
						if(dp[i][j][k][l][t]){
							dp[i+1][j-1][k][l][t]=max(dp[i+1][j-1][k][l][t],dp[i][j][k][l][t]+a[i+1]);
							dp[i+2][j][k-1][l][t]=max(dp[i+2][j][k-1][l][t],dp[i][j][k][l][t]+a[i+2]);
							dp[i+3][j][k][l-1][t]=max(dp[i+3][j][k][l-1][t],dp[i][j][k][l][t]+a[i+3]);
							dp[i+4][j][k][l][t-1]=max(dp[i+4][j][k][l][t-1],dp[i][j][k][l][t]+a[i+4]);
						}
	cout<

 


再看看,发现i这一维根本没用
i可以由j,k,l,t确定 
可以删去
代码如下 

 

 

#include
using namespace std;
int n,m,a[360],b[5];
int dp[42][42][42][42];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	int scan;
	for(int i=1;i<=m;i++){
		cin>>scan;
		b[scan]++;
	}
	dp[0][0][0][0]=a[1];
	for(int j=0;j<=b[1];j++)
		for(int k=0;k<=b[2];k++)
			for(int l=0;l<=b[3];l++)
				for(int t=0;t<=b[4];t++)
					if(dp[j][k][l][t]){
						dp[j+1][k][l][t]=max(dp[j+1][k][l][t],dp[j][k][l][t]+a[j+1+2*k+3*l+4*t+1]);
						dp[j][k+1][l][t]=max(dp[j][k+1][l][t],dp[j][k][l][t]+a[j+2*k+2+3*l+4*t+1]);
						dp[j][k][l+1][t]=max(dp[j][k][l+1][t],dp[j][k][l][t]+a[j+2*k+3*l+3+4*t+1]);
						dp[j][k][l][t+1]=max(dp[j][k][l][t+1],dp[j][k][l][t]+a[j+2*k+3*l+4*t+4+1]);
					}
	cout<

 

 

还可以再优化 
用滚动数组搞搞
减去一维 虽然已经很小了 


同时思维方法也要变换过来
以上两个代码都是以当前一个已经确定的状态去更新后续阶段的状态 
而这样表示不会优化……(果然还是太菜了) 
否则一个当前阶段已经被更新的状态会更新同一个阶段的另一个状态
那就GG了


所以采用计算当前状态的解法

 

 

代码如下: 

 

#include
using namespace std;
int n,m,a[360],b[5];
int dp[2][42][42][42];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	int scan;
	for(int i=1;i<=m;i++){
		cin>>scan;
		b[scan]++;
	}
	dp[0][0][0][0]=a[1];
	for(int j=0;j<=b[1];j++)
		for(int k=0;k<=b[2];k++)
			for(int l=0;l<=b[3];l++)
				for(int t=0;t<=b[4];t++){
						int A=j&1;
						int large=-10000000;
						if(j)large=max(large,dp[A^1][k][l][t]+a[j+2*k+3*l+4*t+1]);
						if(k)large=max(large,dp[A][k-1][l][t]+a[j+2*k+3*l+4*t+1]);
						if(l)large=max(large,dp[A][k][l-1][t]+a[j+2*k+3*l+4*t+1]);
						if(t)large=max(large,dp[A][k][l][t-1]+a[j+2*k+3*l+4*t+1]);
						if(large!=-10000000)dp[A][k][l][t]=large;
					}
	cout<

 

你可能感兴趣的:(简单的DP)