【动态规划、dp】P1541 [NOIP2010 提高组] 乌龟棋 题解

题目描述

小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。

乌龟棋的棋盘是一行 NNN 个格子,每个格子上一个分数(非负整数)。棋盘第 111 格是唯一的起点,第 NNN 格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。

乌龟棋中 MMM 张爬行卡片,分成 444 种不同的类型(MMM 张卡片中不一定包含所有 444 种类型的卡片,见样例),每种类型的卡片上分别标有 1,2,3,41,2,3,41,2,3,4 四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。

游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。

很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。

现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?输入数据保证到达终点时刚好用光 MMM 张爬行卡片。

对于 100%100\%100% 的数据有 1≤N≤350,1≤M≤1201≤N≤350,1≤M≤1201N350,1M120,且 444 种爬行卡片,每种卡片的张数不会超过 4040400≤ai≤100,1≤i≤N,1≤bi≤4,1≤i≤M0≤a_i≤100,1≤i≤N,1≤b_i≤4,1≤i≤M0ai100,1iN,1bi4,1iM

思路

dpi,j,k,ldp_{i,j,k,l}dpi,j,k,l 表示使用 iii 张第 111 种卡片,jjj 张第 222 种卡片,kkk 张第 333 种卡片,lll 张第 444 种卡片的最大得分,题目要求输出的即为 dpl1,l2,l3,l4dp_{l1,l2,l3,l4}dpl1,l2,l3,l4l1,l2,l3,l4l1,l2,l3,l4l1,l2,l3,l4 分别表示四种卡片数量。

边界条件:dp0,0,0,0=a1dp_{0,0,0,0} = a_1dp0,0,0,0=a1,显而易见乌龟棋子自动获得起点格子( 就是不经过任何操作)的分数。

转移方程:容易发现:dpi,j,k,l=max⁡(dpi−1,j,k,l,dpi,j−1,k,l,dpi,j,k−1,l,dpi,j,k,l−1)+ai+j×2+k×3+l×4+1dp_{i,j,k,l} = \max(dp_{i-1,j,k,l},dp_{i,j-1,k,l},dp_{i,j,k-1,l},dp_{i,j,k,l-1}) + a_{i+j\times2+k\times3+l\times4 + 1}dpi,j,k,l=max(dpi1,j,k,l,dpi,j1,k,l,dpi,j,k1,l,dpi,j,k,l1)+ai+j×2+k×3+l×4+1,至于 ai+j×2+k×3+l×4+1a_{i+j\times2+k\times3+l\times4 + 1}ai+j×2+k×3+l×4+1 而不是 ai+j×2+k×3+l×4a_{i+j\times2+k\times3+l\times4}ai+j×2+k×3+l×4,是因为我们保存时起始点是 a1a_1a1

时间复杂度 O(l14)O(l1^4)O(l14) ,空间可采用滚动数组优化成三次方的,但是我懒
在这里插入图片描述

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int dp[41][41][41][41];
int n,m;
int a[355];
int l1,l2,l3,l4;
signed main() {
	scanf("%lld %lld",&n,&m);
	for(int i = 1;i <= n;i++) scanf("%lld",&a[i]);
	while(m--) {
		int inp;
		scanf("%lld",&inp);
		if(inp == 1) l1++;
		else if(inp == 2) l2++;
		else if(inp == 3) l3++;
		else l4++;
	}
	dp[0][0][0][0] = a[1];
	for(int i = 0;i <= l1;i++) {
		for(int j = 0;j <= l2;j++) {
			for(int k = 0;k <= l3;k++) {
				for(int l = 0;l <= l4;l++) {
					if(i + j + k + l == 0) continue;
					if(i != 0) dp[i][j][k][l] = max(dp[i][j][k][l],dp[i - 1][j][k][l]);
					if(j != 0) dp[i][j][k][l] = max(dp[i][j][k][l],dp[i][j - 1][k][l]);
					if(k != 0) dp[i][j][k][l] = max(dp[i][j][k][l],dp[i][j][k - 1][l]);
					if(l != 0) dp[i][j][k][l] = max(dp[i][j][k][l],dp[i][j][k][l - 1]);
					dp[i][j][k][l] += a[i + j * 2 + k * 3 + l * 4 + 1];
				}
			}
		}
	}
	printf("%lld\n",dp[l1][l2][l3][l4]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值