ABC 176 F - Brave CHAIN

89 篇文章 1 订阅

题目传送门
我们不妨可以重新理解题意:

3 ∗ n 3*n 3n张牌,一开始你拿走了 1 到 5 1到5 15号的牌,然后你舍弃三张,如果这三张点数相同,则分数加1。然后再拿三张牌,再舍弃三张如果这三张点数相同,则分数加1……最终的最大得分为多少?

首先可以很容易想到一个 d p : dp: dp:
d p i , j , k dp_{i,j,k} dpi,j,k表示考虑到第i轮,结束后手里拿的牌是 i i i j j j的最大得分为多少?
显然的转移方程: d p i , j , k = d p i − 1 , j ′ , k ′ + 丢 弃 的 牌 是 否 一 样 dp_{i,j,k}=dp_{i-1,j',k'}+丢弃的牌是否一样 dpi,j,k=dpi1,j,k+,这样直接做的话是 O ( n 5 ) O(n^5) O(n5)肯定爆炸。那么我们想优化。
如果第 i i i轮结束后两张牌都是从第 i i i轮抽中的牌值取出来的那么 i ′ , j ′ i',j' i,j就可以随意,我们可以设 b e s t = max ⁡ { d p i − 1 , j ′ , k ′ } , 第 i 轮 取 走 那 两 张 牌 剩 下 的 是 p best=\max \{dp_{i-1,j',k'}\},第i轮取走那两张牌剩下的是p best=max{dpi1,j,k}ip d p i , j , k = max ⁡ { b e s t , d p i − 1 , p , p } dp_{i,j,k}=\max\{best,dp_{i-1,p,p}\} dpi,j,k=max{best,dpi1,p,p}
同理我们也可以对去出一张牌的情况进行优化,优化完的复杂度为 O ( n 3 ) O(n^3) O(n3)因为转移变成 O ( 1 ) O(1) O(1)的了,可是这样还是过不了,比赛的时候我也就卡在这里了。
其实我们发现每次你只会拿到3张牌,结束后你如果在这一轮中拿出了牌的话,那两张牌一定会有这三张牌中之一。所以我们的有用状态就可以变成 O ( n ) O(n) O(n)级别的了。滚动掉第一维,时间复杂度就可以变成 O ( n 2 ) O(n^2) O(n2)的了。
Code:

/*
{By GWj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define check_min(a,b) a=min((a),(b))
#define check_max(a,b) a=max((a),(b))
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
const int MAXN=2002;
int dp[MAXN][MAXN],save[MAXN],save2[MAXN][MAXN],delta/*全局增量*/,best,best2[MAXN];//滚动掉第一维 
int best_,best2_[MAXN];
int n,a[MAXN*3];
vector<vector<int> > cards;
int main(){
	fastio;
	R(n);
	n=n+n+n;
	rb(i,1,n)
		R(a[i]);
	if(n==3){
		int rest=1;
		rb(i,1,n){
			if(a[i]==a[1]);
			else rest=0;
		}
		cout<<rest<<endl;
		return 0;
	}	
	rb(j,1,n/3){
		vector<int> tmp;
		if(j==1){
			rb(i,1,5){
				tmp.PB(a[i]);
			}
			cards.PB(tmp); 
		}	
		else{
			if(j==n/3){
				tmp.PB(a[n]);
				cards.PB(tmp);
			}
			else{
				rb(i,(j-2)*3+5+1,(j-1)*3+5){
					tmp.PB(a[i]);
				}
				cards.PB(tmp);
			}
		}
	}
	memset(dp,-63,sizeof(dp));
	rep(i,5)
		rep(j,5)
		{
			if(i!=j){
				bool ok=1;
				int las=-1;
				rep(k,5){
					if(k==i||k==j) continue;
					if(las!=-1&&las!=cards[0][k]){
						ok=0;
					}
					las=cards[0][k];
				}
				dp[cards[0][i]][cards[0][j]]=dp[cards[0][j]][cards[0][i]]=ok;
			}
		}
	best=-INF;
	memset(best2,-63,sizeof(best2));
	n/=3;
	rb(i,1,n)
	{
		rb(j,1,n){
			check_max(best2[i],dp[i][j]);
		}
		check_max(best,best2[i]);
	}
	rb(i,1,n-2){
		rb(j,1,n)
			save[j]=dp[j][j];
		rep(j,3)
			rb(k,1,n)
				save2[cards[i][j]][k]=dp[cards[i][j]][k];
		best_=best;
		rb(j,1,n)
			best2_[j]=best2[j];
		bool ok=1;
		rep(j,3)
		{
			if(cards[i][j]==cards[i][0]);
			else{
				ok=0;
			}
		}
		if(ok){
			delta++;
			continue;
		}
		/*枚举只有拿了1张牌的情况*/
		rep(j_,3){//拿走哪一张
			//枚举另一张
			int j=cards[i][j_];
			int need=-1;
			rep(k_,3){
				int k=cards[i][k_];
				if(k_!=j_){
					if(need==-1){
						need=k;
					}
					else{
						if(need!=k) need=-1;
					}
				}
			}
			rb(k,1,n){
				check_max(dp[j][k],best2[k]);
				check_max(dp[k][j],best2[k]);
				if(need>0)
				check_max(dp[k][j],save2[need][k]+1);
				check_max(dp[j][k],dp[k][j]);
				check_max(best2_[k],dp[j][k]);
				check_max(best2_[j],dp[j][k]);
				check_max(best_,dp[j][k]);
			} 
		}
		/*枚举拿走两张的情况*/
		rep(j_,3)
			rep(k_,3){
				if(j_==k_) continue;
				int need=0;
				rep(l_,3)
					if(l_!=j_&&l_!=k_){
						need=cards[i][l_];
					}
				int j=cards[i][j_],k=cards[i][k_];
				check_max(dp[j][k],best);
				check_max(dp[j][k],save[need]+1);
				check_max(dp[k][j],dp[j][k]);
				check_max(best2_[k],dp[j][k]);
				check_max(best2_[j],dp[j][k]);
				check_max(best_,dp[j][k]);
			}
		best=best_;
		rb(j,1,n)
			best2[j]=best2_[j],assert(best>=best2[j]);
	}	
	rb(i,1,n)
		rb(j,1,n)
		{
			assert(dp[i][j]==dp[j][i]); 
		}
	check_max(best,dp[cards[n-1][0]][cards[n-1][0]]+1);
	cout<<best+delta<<endl;
	return 0;
}/*
5
1 2 5 3 3 5 3 1 3 4 3 2 5 1 4 
*/

代码感觉挺好写的,就是初始化的时候一定要设置成 − ∞ -\infty ,不然会挂。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值