前言
乌龟棋是NOIP2010提高组的原题,是一类典型线性dp的代表题目。
题意
给定 n n n个数,从左边开始走到右边。共有 m m m张卡片,分成 4 4 4种不同的类型,每种类型的卡片上分别标有 1 、 2 、 3 、 4 1、2、3、4 1、2、3、4四个数字之一,表示使用这种卡片后,将向前行走相应的格子数。每次走到哪个格子,就会获得对应的数字,问走到最后,获得数字总和最大为多少。
数据范围
1 ≤ m ≤ 350 1 \leq m \leq 350 1≤m≤350
1 ≤ n ≤ 120 1 \leq n \leq 120 1≤n≤120
每种卡片最多有 40 40 40张。
思路
f ( i , j , k , l ) f(i,j,k,l) f(i,j,k,l)表示四种卡片分别使用了若干张的走法的最大值。
按最后一步选择哪张卡片分成四类,以第一类卡片为例,若最后一步为 1 1 1,则前一个状态为 f ( i − 1 , j , k , l ) f(i - 1,j,k,l) f(i−1,j,k,l)。
状态转移方程为, f ( i , j , k , l ) = m a x ( f ( i , j , k , l ) , f ( i − 1 , j , k , l ) + a [ i + j ∗ 2 + k ∗ 3 + l ∗ 4 ] ) f(i,j,k,l) = max(f(i,j,k,l),f(i-1,j,k,l) + a[i+j*2+k*3+l*4]) f(i,j,k,l)=max(f(i,j,k,l),f(i−1,j,k,l)+a[i+j∗2+k∗3+l∗4])
代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 360, M = 45;
int n, m;
int a[N];
int f[M][M][M][M];
int main()
{
int b[5] = {
0};
cin >> n >> m;
for(int i=0;i<n;i++) cin >> a[i];
for(int i=1;i<=m;i++){
int x;
cin >> x;
b[x] ++;
}
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 l=0;l<=b[4];l++)
{
int tot = a[i+j*2+k*3+l*4];
f[i][j][k][l] = tot;
if(i) f[i][j][k][l] = max(f[i][j][k][l], f[i-1][j][k][l]+tot);
if(j) f[i][j][k][l] = max(f[i][j][k][l], f[i][j-1][k][l]+tot);
if(k) f[i][j][k][l] = max(f[i][j][k][l], f[i][j][k-1][l]+tot);
if(l) f[i][j][k][l] = max(f[i][j][k][l], f[i][j][k][l-1]+tot);
}
cout << f[b[1]][b[2]][b[3]][b[4]] << endl;;
return 0;
}