麻将 hongmah

版权声明:写得不好,随便转载,但请注明出处,感激不尽 https://blog.csdn.net/xyc1719/article/details/88382752

题目来源:CF1110D
【简要题意】有n个数集合(多重集),每个数不超过m。可以分成{i,i,i}或{i-1,i,i+1}的三元组,求最多分成几份三元组。n、m<=1e6

【分析】
考虑动态规划,写出一个 Θ ( n 3 ) \Theta(n^3) 的动归方程,放下转贪心。试了好几种贪心策略都被hack,并且对于贪心证明毫无头绪。
回到dp,结果是一种对于状态定义的优化:定义f[i][j][k]表示{i-1,i,i+1}有j个{i,i+1,i+2}有k个。可以证明0<j,k<3。如果j或k有3个及以上,那么可以归类到3个{i,i,i}的形式。
转移方程: f [ i ] [ k ] [ l ] = max 0 &lt; = i , j , k &lt; 3 ( f [ i ] [ k ] [ l ] , f [ i 1 ] [ j ] [ k ] + a [ i ] j k l 3 + l ) f[i][k][l] = \max\limits_{0&lt;=i,j,k&lt;3} (f[i][k][l],f[i-1][j][k]+\frac{a[i]-j-k-l}{3}+l)
对于剩余的i转移成{i,i,i}的形式,主要是{i,i+1,i+2}的干扰。枚举完和i有关的三个三元组之后自然可以将剩下的部分悉数转移而不用担心状态并非最优了。
我认为这道题的最大的特点就是动归状态的定义依赖于三个操作的个数,而并未操作后剩下的部分。这种思路对于那些“状态”之间有所纠缠,而操作本身并不复杂的动归题目的状态定义有启发意义。

【code】

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e6+1000;
int n,m,a[maxn],cnt=0;
int f[maxn][3][3];
inline void read(int &x){
	x=0;int fl=1;char tmp=getchar();
	while(tmp<'0'||tmp>'9'){if(tmp=='-')fl=-fl;tmp=getchar();}
	while(tmp>='0'&&tmp<='9')x=(x<<1)+(x<<3)+tmp-'0',tmp=getchar();
	x=x*fl;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){int x;read(x);a[x]++;}
	memset(f,-1,sizeof(f));f[0][0][0]=0;
	for(int i=1;i<=m;i++)
		for(int j=0;j<3;j++)
			for(int k=0;k<3;k++)
				for(int l=0;l<3;l++)
				if(a[i]<j+k+l)continue;
				else f[i][k][l]=max(f[i][k][l],f[i-1][j][k]+(a[i]-j-k-l)/3+l);
	cout<<f[m][0][0]<<endl;
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/xyc1719/article/details/88382752
今日推荐