题面:有 n n n 张扑克牌堆成一个栈,从上往下第 i i i 张花色是 c i c_i ci,点数是 a i a_i ai,价值是 v i v_i vi。每次选择拿走从上往下第1张或第3张,拿走的牌必须和上一次拿走的花色或者点数一样。请问如何拿牌,才能使拿出来的牌价值和最大。
数据范围: 1 ≤ 1\le 1≤ n , c i , a i n,c_i,a_i n,ci,ai ≤ \le ≤ 500 500 500, 1 ≤ 1\le 1≤ v i v_i vi ≤ \le ≤ 1 0 6 10^6 106
先从题目的性质入手,看这两种取牌情况:
发现不论取走第三张牌还是第一张牌之后,栈中从上往下第三张牌都是原先第三张牌的下一张,所以我们可以以第三张牌位置+1的时间轴来进行,分拿第一张牌和第三张牌的情况进行dp
一、状态
我们需要什么状态?首先为判合法性,需要明确上一次取了哪张牌,其次需要明确第一、二、三张牌都是什么。
设
f
i
,
j
,
k
f_{i,j,k}
fi,j,k 表示第三张牌为
i
i
i ,第一张牌为
j
j
j ,第二张牌为
k
k
k,上一次拿的牌是
i
−
1
i-1
i−1 的最大价值和。
g
i
,
j
,
k
g_{i,j,k}
gi,j,k 表示第三张牌为
i
i
i,第一张牌为
j
j
j,第二张牌为
i
−
1
i-1
i−1,上一次拿的牌是
k
k
k 的最大价值和。
f
f
f 的状态表示的是形如
j
,
k
,
i
,
i
+
1
,
i
+
2...
j,k,i,i+1,i+2...
j,k,i,i+1,i+2... 的栈
g
g
g 的状态表示的是形如
j
,
i
,
i
+
1
,
i
+
2...
j,i,i+1,i+2...
j,i,i+1,i+2... 的栈
二、转移
状态的转移需要考虑拿走一张牌后第一、二、三张牌以及上次取的牌的变化,具体如下:
(1)若要取走第三张牌(即取走 i i i),对下一个状态而言 i − 1 i-1 i−1 就是上一次取的牌,用 数组 f f f 转移
-
上次取的是位置 i − 1 i-1 i−1 的牌:从第一张为 j j j,第二张为 k k k,第三张为 i i i,上次取的是位置 i − 1 i-1 i−1 的牌—转移到—>第一张为 j j j,第二张为 k k k,第三张为 i + 1 i+1 i+1 ,上次取的是位置 i i i 的牌
f [ i + 1 ] [ j ] [ k ] = m a x ( f [ i + 1 ] [ [ j ] [ k ] , f [ i ] [ j ] [ k ] + v [ i ] ) f[i+1][j][k]=max(f[i+1][[j][k],f[i][j][k]+v[i]) f[i+1][j][k]=max(f[i+1][[j][k],f[i][j][k]+v[i])
-
上次取的是位置 k k k 的牌:从第一张为 j j j,第二张为 i − 1 i-1 i−1,第三张为 i i i,上次取的是位置 k k k的牌 —转移到—>第一张为 j j j,第二张为 i − 1 i-1 i−1,第三张为 i + 1 i+1 i+1,上次取的是位置 i − 1 i-1 i−1 的牌
f [ i + 1 ] [ j ] [ i − 1 ] = m a x ( f [ i + 1 ] [ j ] [ i − 1 ] , g [ i ] [ j ] [ k ] + v [ i ] ) f[i+1][j][i-1]=max(f[i+1][j][i-1],g[i][j][k]+v[i]) f[i+1][j][i−1]=max(f[i+1][j][i−1],g[i][j][k]+v[i])
(2)若要取走第一张牌(即取走 j j j),对下一个状态而言 j j j 就是上一次拿的牌,用数组 g g g 来转移
-
上次取的是位置 i − 1 i-1 i−1 的牌:从第一张为 j j j,第二张为 k k k,第三张为 i i i,上次取了位置 i − 1 i-1 i−1的牌 —转移到–> 第一张为 k k k,第二张为 i i i,第三张为 i + 1 i+1 i+1 ,上次取的是位置 j j j 的牌
g [ i + 1 ] [ k ] [ j ] = m a x ( g [ i + 1 ] [ k ] [ j ] , f [ i ] [ j ] [ k ] + v [ j ] ) g[i+1][k][j]=max(g[i+1][k][j],f[i][j][k]+v[j]) g[i+1][k][j]=max(g[i+1][k][j],f[i][j][k]+v[j])
-
上次取的是位置 k k k 的牌:从第一张为 j j j,第二张为 i − 1 i-1 i−1,第三张为 i i i,上次取了位置 k k k 的牌—转移到—>第一张为 i − 1 i-1 i−1,第二张为 i i i,第三张为 i + 1 i+1 i+1,上次取了位置是 j j j的牌
g [ i + 1 ] [ i − 1 ] [ j ] = m a x ( g [ i + 1 ] [ i − 1 ] [ j ] , g [ i ] [ j ] [ k ] + v [ j ] ) g[i+1][i-1][j]=max(g[i+1][i-1][j],g[i][j][k]+v[j]) g[i+1][i−1][j]=max(g[i+1][i−1][j],g[i][j][k]+v[j])
AC代码:
#include <bits/stdc++.h>
using namespace std;
int n,c[505],a[505],v[505],f[2][505][505],g[2][505][505],ans;
inline bool check(int x,int y){return !x||c[x]==c[y]||a[x]==a[y];}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d%d%d",&c[i],&a[i],&v[i]);
for(int i=1;i<=3;i++)v[++n]=-2139062144;
memset(f,128,sizeof(f));memset(g,128,sizeof(g));
g[0][1][0]=0;
for(int i=3,o=0;i<=n;i++,o^=1){
memset(f[o^1],128,sizeof(f[o^1]));memset(g[o^1],128,sizeof(g[o^1]));
for(int j=0;j<=n;j++)
for(int k=0;k<=n;k++){
if(f[o][j][k]>=0){
ans=max(ans,f[o][j][k]);
if(check(i-1,i))f[o^1][j][k]=max(f[o^1][j][k],f[o][j][k]+v[i]);
if(check(i-1,j))g[o^1][k][j]=max(g[o^1][k][j],f[o][j][k]+v[j]);
}
if(g[o][j][k]>=0){
ans=max(ans,g[o][j][k]);
if(check(k,i))f[o^1][j][i-1]=max(f[o^1][j][i-1],g[o][j][k]+v[i]);
if(check(k,j))g[o^1][i-1][j]=max(g[o^1][i-1][j],g[o][j][k]+v[j]);
}
}
}
printf("%d\n",ans);
return 0;
}