[ABC176F] Brave CHAIN
[ABC176F] Brave CHAIN
题意
给你 \(3n\) 个数字。每次你可以选取前 \(5\) 个数字,拿走里面任意三个数字,剩下两个,如果拿走的 \(3\) 个数字相等,得分 \(+1\)。问最大得分是多少。
思路
首先我们想尝试贪心。然而不好贪心,因为你不知道前面会给你留下哪两张牌,留下的方案数很多。
题目可以看做每次有三个新数字,和前面两个留下的数字进行操作,它们中留下两个。
此时可以考虑 DP。由前面思考或者部分分提示可以设出 \(dp_{i,x,y}\) 表示考虑到第 \(i\) 组数字(\(3\) 个为一组),留下 \(x,y\) 的最大分数。
于是我们要枚举前面留下哪两个数字,五个数字中留下哪两个数字,状态是 \(O(nV^2)\),转移考虑一下留下哪两个数字,是常数复杂度,总共 \(O(nV^2)\)。
考虑如何优化。
首先枚举 \(i\) 是必须的,不过空间上可以把这一维滚掉。状态感觉都很有用。
考虑优化转移。分为得分和不得分两种情况考虑,这样代码好写一些吧。设新的三个数字为 \(a,b,c\)。若 \(a=b=c\),我们直接删除这三个数字得分 \(+1\),且这样是一定不劣的。反正它们迟早都要删掉,就不必留到后面了。因此这种情况全局 \(+1\),可以维护一个 tag。虽然存在不得分的更新方式,但是根据贪心,我们最后的最大得分方案中一定没有把这三个新数留下的情况,这是不优的,因此这种情况不用考虑不得分的转移。
若 \(a,b,c\) 有两个相等,设 \(a=b\)。可以在前面找一个 \(x=a=b,y\in[1,n]\),进行转移,剩下的牌就是 \(c,y\),得分 \(+1\)。也可以在前面找 \(x=y=c\),剩下 \(a,b\),得分 \(+1\)。
若 \(a,b,c\) 互不相同,可以在前面找 \(x=y=a \operatorname{or} b\operatorname{or} c\),这里假设选择了 \(a\)。那么剩下的是 \(b,c\),得分 \(+1\)。
以上转移都是 \(O(n)\) 或 \(O(1)\) 的。下面讨论不得分的情况。
为了方便,不得分的情况可以不用考虑 \(a,b,c\) 相等的情况。虽然这样可能会把得分的情况算入不得分,但由于我们的 \(dp\) 值是取 \(\max\),所以没有关系。
若新数留下两个假设是 \(a,b\),那么剩下状态是 \(a,b\),得分不变。得分等于 \(x,y\) 任取的最大值,可以在上一层 \(dp\) 顺便维护,时间复杂度是 \(O(1)\) 的。
若剩下 \(a,x\),则剩下状态为 \(a,x\),我们必须枚举 \(x\),毕竟它是目前一层的状态。然后对所有可能的 \(y\in[1,n]\) 取最大值,这个也可以在上一层 DP 的时候顺便维护。时间复杂度 \(O(n)\)。
若剩下 \(x,y\),则我们要枚举所有 \(x,y\),因为这是状态。但是你发现上面所有的转移都最多只有 \(O(n)\),而且这个转移极为美丽,它是由 \(x,y\to x,y\) 的。一个小技巧是把上面的转移用临时数组存下来,只有 \(O(n)\) 个,做完上面转移后直接把数组盖到原 DP 数组上,这样我们就不用 copy 一遍 \(n^2\) 的 DP 数组了。
总时间复杂度 \(O(n^2)\)。
code
#include<bits/stdc++.h>
//#define LOCAL
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
#define isdigit(x) (x>='0'&&x<='9')
using namespace std;
typedef long long ll;
const int N=3e3+7;
template <typename T>
inline void read(T &x) {
	x=0;
	char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	for(;isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
}
template <typename T>
inline void write(T x) {
	static int st[10];
	int top=0;
	do {
		st[top++]=x%10;
		x/=10;
	}while(x);
	while(top) putchar(st[--top]+'0');
}
template <typename T>
inline void write(T x,char ch) {
	write(x),putchar(ch);
}
int n;
int a[N*3];
int f[N][N],lamax,g[N],s;
void update(int &mx,int &mx1,int &mx2,int &a,int b,int c) {
	if(b) a=max(a,b+c);
	mx=max(mx,a);mx1=max(mx1,a),mx2=max(mx2,a);
}
struct node{
	int x,y,z,p;
};
vector<node> vec;
int main() {
	#ifdef LOCAL
	freopen("in.txt","r",stdin);
	freopen("my.out","w",stdout);
	#else
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	#endif
	read(n);
	rep(i,1,n*3) {
		read(a[i]);
	}
	int x=a[1],y=a[2];
	if(x>y) swap(x,y);
	f[x][y]=1;
	lamax=1,g[x]=g[y]=1;
	rep(i,1,n-1) {
		vec.clear();
		int b[3]={a[i*3],a[i*3+1],a[i*3+2]};
		if(b[0]==b[1]&&b[0]==b[2]) { s++;continue;}
		if(b[0]==b[1]||b[0]==b[2]||b[1]==b[2]) {
			int c1=0,c3=0;
			if(b[0]==b[1]) c1=0,c3=2;
			else if(b[0]==b[2]) c1=0,c3=1;
			else c1=1,c3=0;
			rep(k,1,n) {
				int x=k,y=b[c3];
				if(x>y) swap(x,y);
				int xx=k,yy=b[c1];
				if(xx>yy) swap(xx,yy);
				vec.push_back({x,y,f[xx][yy],1});
			}
			vec.push_back({b[c1],b[c1],f[b[c3]][b[c3]],1});
		}else {
			int x=b[1],y=b[2];if(x>y) swap(x,y);
			vec.push_back({x,y,f[b[0]][b[0]],1});
			x=b[0],y=b[2];if(x>y) swap(x,y);
			vec.push_back({x,y,f[b[1]][b[1]],1});
			x=b[0],y=b[1];if(x>y) swap(x,y);
			vec.push_back({x,y,f[b[2]][b[2]],1});
		}
		int x=b[1],y=b[2];if(x>y) swap(x,y);
		vec.push_back({x,y,lamax,0});
		x=b[0],y=b[2];if(x>y) swap(x,y);
		vec.push_back({x,y,lamax,0});
		x=b[0],y=b[1];if(x>y) swap(x,y);
		vec.push_back({x,y,lamax,0});
		rep(k,1,n) {
			rep(c,0,2) {
				int x=b[c],y=k;
				if(x>y) swap(x,y);
				vec.push_back({x,y,g[k],0});
			}
		}
		for(auto tmp : vec) {
			update(lamax,g[tmp.x],g[tmp.y],f[tmp.x][tmp.y],tmp.z,tmp.p);
		}
	}
	int ans=lamax+s;
	if(f[a[n*3]][a[n*3]]) ans=max(ans,f[a[n*3]][a[n*3]]+s+1);
	pf("%d\n",ans-1);
}
本文来自博客园,作者:wing_heart,转载请注明原文链接:https://www.cnblogs.com/wingheart/p/18438129

                
            
        
浙公网安备 33010602011771号