[JOISC 2015Day3] card game is great fun

题目链接:LOJ3002

题面:有 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 i1 的最大价值和。
g i , j , k g_{i,j,k} gi,j,k 表示第三张牌为 i i i,第一张牌为 j j j,第二张牌为 i − 1 i-1 i1,上一次拿的牌是 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 i1 就是上一次取的牌,用 数组 f f f 转移

  • 上次取的是位置 i − 1 i-1 i1 的牌:从第一张为 j j j,第二张为 k k k,第三张为 i i i,上次取的是位置 i − 1 i-1 i1 的牌—转移到—>第一张为 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 i1,第三张为 i i i,上次取的是位置 k k k的牌 —转移到—>第一张为 j j j,第二张为 i − 1 i-1 i1,第三张为 i + 1 i+1 i+1,上次取的是位置 i − 1 i-1 i1 的牌

    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][i1]=max(f[i+1][j][i1],g[i][j][k]+v[i])

(2)若要取走第一张牌(即取走 j j j),对下一个状态而言 j j j 就是上一次拿的牌,用数组 g g g 来转移

  • 上次取的是位置 i − 1 i-1 i1 的牌:从第一张为 j j j,第二张为 k k k,第三张为 i i i,上次取了位置 i − 1 i-1 i1的牌 —转移到–> 第一张为 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 i1,第三张为 i i i,上次取了位置 k k k 的牌—转移到—>第一张为 i − 1 i-1 i1,第二张为 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][i1][j]=max(g[i+1][i1][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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值