[NOIP2010 提高组] 乌龟棋 (线性dp)
题意
小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。
乌龟棋的棋盘只有一行,该行有 N 个格子,每个格子上一个分数(非负整数)。
棋盘第 1 格是唯一的起点,第 N 格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。
乌龟棋中共有 M 张爬行卡片,分成 4 种不同的类型(M 张卡片中不一定包含所有 4 种类型的卡片),每种类型的卡片上分别标有1、2、3、4 四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。
游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。
游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。
玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。
很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。
现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?
\(1≤N≤350,1≤M≤120,0≤a_i≤100,1≤b_i≤4,\)
思路:
一开始想的\(5\)维的\(dp\),\(dp[i][j][k][x][y]\)表示走前\(i\)个一共用了\(j\)个数字为\(1\)的卡片,\(k\)个数字为\(2\)的卡片,\(x\)个数字为\(3\)的卡片,\(y\)个数字为\(4\)的卡片所得到得最大分数。
注意到\(dp[i]\)只会由\(dp[i-4],dp[i-3],dp[i-2],dp[i-1]\)转移而来,所以可以考虑滚动数组,空间复杂度为\(4*40*40*40*40=10240000\),勉强开得下,但是时间会炸,时间复杂度为\(O(40^4*n)\),于是此方法行不通,gg。
考虑\(dp[i][j][k][z]\)表示一共用了\(i\)个数字为\(1\)的卡片,\(j\)个数字为\(2\)的卡片,\(k\)个数字为\(3\)的卡片,\(z\)个数字为\(4\)的卡片所得到得最大分数。
则\(dp[i][j][k][z]=max(dp[i-1][j][k][z],dp[i][j-1][k][z],dp[i][j][k-1][z],dp[i][j][k][z-1])+a[i+j*2+k*3+z*4]\)
将\(a\)数组从\(0\)开始输入,方便处理,再注意一下边界,最后答案为\(dp[cnt[1][cnt[2]][cnt[3]][cnt[4]]+a[0]\)。
时间复杂度为:\(O(40^4*n)\)
空间复杂度为:\(O(40^4*n)\)
Code:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <cassert>
#include<bits/stdc++.h>
#define debug(x) cout<<#x<<" :"<<x<<endl
using namespace std;
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define fi first
#define eps 1e-6
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll mod=1e4+7;
ll powmod(ll a,ll b,ll mod1) {ll res=1;a%=mod1; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod1;a=a*a%mod1;}return res;}
const int maxn=2e5+10;
const int MAXN=2e5+10;
ll q[maxn];
ll n,m;
ll qpow(ll a,ll b){
ll ans=1;
while(b){
if(b&1)ans=ans*a;
a=a*a;
b>>=1;
}
return ans;
}
/*
dp[i,j,k,z]=dp[i-1,j,k,z]+a[i+j*2+k*3+z*4]...
*/
ll a[maxn],b[maxn];
ll dp[41][41][41][41];
ll cnt[maxn];
int main(){
int t;
srand(time(NULL));
//scanf("%d",&t);
t=1;
while(t--){
cin>>m>>n;
rep(i,0,m-1)cin>>a[i];
rep(i,1,n)cin>>b[i];
rep(i,1,n){
cnt[b[i]]++;
}
if(n==1){
cout<<a[0]<<"\n";
continue;
}
rep(i,0,cnt[1]){
rep(j,0,cnt[2]){
rep(k,0,cnt[3]){
rep(z,0,cnt[4]){
if(i){
dp[i][j][k][z]=max(dp[i-1][j][k][z]+a[i+j*2+k*3+z*4],dp[i][j][k][z]);
}
if(j){
dp[i][j][k][z]=max(dp[i][j-1][k][z]+a[i+j*2+k*3+z*4],dp[i][j][k][z]);
}
if(k){
dp[i][j][k][z]=max(dp[i][j][k-1][z]+a[i+j*2+k*3+z*4],dp[i][j][k][z]);
}
if(z){
dp[i][j][k][z]=max(dp[i][j][k][z-1]+a[i+j*2+k*3+z*4],dp[i][j][k][z]);
}
}
}
}
}
ll maxx=0;
maxx=max(maxx,dp[cnt[1]][cnt[2]][cnt[3]][cnt[4]]);
cout<<maxx+a[0]<<"\n";
}
return 0;
}
/*
9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1
73
*/