【NOIP2010】【DP】T2 乌龟棋 题解

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

乌龟棋中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= M1bi
【输出】
输出文件名tortoise.out。
输出只有1 行,1 个整数,表示小明最多能得到的分数。
【输入输出样例1】
tortoise.in
9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1
tortoise.out
73
【输入输出样例 1 说明】
小明使用爬行卡片顺序为1,1,3,1,2,得到的分数为6+10+14+8+18+17=73。注意,
由于起点是1,所以自动获得第1 格的分数6。
【输入输出样例2】
tortoise.in
13 8
4 96 10 64 55 13 94 53 5 24 89 8 30
1 1 1 1 1 2 4 1
tortoise.out
455
【数据范围】
对于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−1= M1bi

明显的Dp题,最直观的想到Dp[350][40][40][40][40]的小暴力。但是会爆空间,有两种解决方法。
1.我们发现DP只对于前4位有影响,开滚动数组。
2.因为保证了N−1= M1bi ,所以我们当前的位置可以有卡片得到,将第一维去掉。
这之后我们就可以继续Dp了,方程f[x1][x2][x3][x4]=max(f[x1-1][x2][x3][x4],f[x1][x2-1][x3][x4],f[x1][x2][x3-1][x4],f[x1][x2][x3][x4-1])+a[x1+x2*2+x3*3+x4*4]。

附代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <string>
#include <iomanip>
#include <ctime>
#include <climits>
#include <cctype>
#include <algorithm>
#define clr(x) memset(x,0,sizeof(x))
#define ms(a,x) memset(x,a,sizeof(x))
#define INF 2100000000
#define LL long long
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif

using namespace std;

const int maxn = 355;
int n,m,s[4];
int f[41][41][41][41];
bool vis[41][41][41][41];
int *val;

inline int dfs(int a, int b, int c, int d) {
    if(vis[a][b][c][d]) return f[a][b][c][d];
    vis[a][b][c][d] = true;
    int pos = a*1+b*2+c*3+d*4+1;
    int aa = 0, bb = 0, cc = 0, dd = 0;
    if(a > 0) aa = dfs(a - 1, b, c, d);
    if(b > 0) bb = dfs(a, b - 1, c, d);
    if(c > 0) cc = dfs(a, b, c - 1, d);
    if(d > 0) dd = dfs(a, b, c, d - 1);
    int maxx = aa;
    if(maxx < bb) maxx = bb;
    if(maxx < cc) maxx = cc;
    if(maxx < dd) maxx = dd;
    f[a][b][c][d] = max(f[a][b][c][d], maxx+val[pos]);
    return f[a][b][c][d];
}

inline void init() {
    clr(f); clr(s); memset(vis, false, sizeof(vis));
    scanf("%d%d",&n,&m);
    val = new int[(const int)(n + 1)];
    for(int i = 1; i <= n; i++) scanf("%d",&val[i]);
    int a;
    for(int i = 1; i <= m; i++) scanf("%d",&a), s[a-1]++;
    f[0][0][0][0] = val[1];
    vis[0][0][0][0] = true;
}

int main() {
    freopen("tortoise.in","r",stdin);
    freopen("tortoise.out","w",stdout);
    init();
    printf("%d",dfs(s[0], s[1], s[2], s[3]));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值