Codeforces 1110D. Jongmah 动态规划

原文链接https://www.cnblogs.com/zhouzhendong/p/CF1110D.html

题意

  给定 n 个数,每一个数都是在 [1,m] 里的整数。

  从中取出形如 {x,x,x} 或者 {x-1,x,x+1} 的集合,各个集合不能相交,问最多能取出几个。

  $n,m\leq 10^6$

题解

  标算非常简洁。

  我这里讲讲我的做法,尽管相对复杂。

  首先,我们可以忽略对于同一个 x ,取出大于2次 {x-1,x,x+1} 这种情况,因为这种可以用取 {x-1,x-1,x-1} {x,x,x} {x+1,x+1,x+1} 来代替。

  所以一个x 最多被形如 {a-1,a,a+1} 的pair 取 6 次。即 {x-2,x-1,x}*2   , {x-1,x,x+1}*2 ,  {x,x+1,x+2}*2  。

  现在来证明一个重要结论:

  如果 x 的个数大于 7 ,那么至少可以取出一次 {x,x,x} 。(也就是说我们可以先不断取 {x,x,x} 这种pair,直到所有数字的出现次数都小于等于7)

  设 t[x] 表示 x 的出现次数。

  对于 t[x] = 8 的情况,考虑到它最多被形如 {a-1,a,a+1} 的这种pair取到6次,所以如果不取 {x,x,x} ,至少会多出2个。如果取了 {x,x,x} ,那么最多会影响一个形如 {a-1,a,a+1} 的pair,导致另外两个数不能取了,但是如果不取 {x,x,x} 也会多出两个,所以至少不亏。

  对于 t[x]>8 的情况就更加显然了。

  于是现在 t[x] < 8 。 考虑 dp ,设 dp[i][j][k] 表示  [1,i] 中,i 被取了 j 次, i + 1 被取了 k 次,且 [1,i-1] 的数被取的次数没有超限,  [i+2,m] 的数没有被取过, 这种情况下取出的pair的最大个数。直接枚举转移暴力dp就好了。

  时间复杂度 O(n) *  大常数。

  实测可过。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL read(){
	LL x=0,f=0;
	char ch=getchar();
	while (!isdigit(ch))
		f|=ch=='-',ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
const int N=1000005;
int n,m;
int t[N],ans=0;
int dp[N][8][8];
void ckmax(int &x,int y){
	x=x>y?x:y;
}
int main(){
	n=read(),m=read();
	for (int i=1;i<=n;i++)
		t[read()]++;
	for (int i=1;i<=m;i++)
		while (t[i]>7)
			t[i]-=3,ans++;
	for (int i=0;i<=m;i++)
		for (int a=0;a<=7;a++)
			for (int b=0;b<=7;b++)
				dp[i][a][b]=-N*2;
	dp[0][0][0]=0;
	for (int i=0;i<m;i++)
		for (int a=0;a<=7;a++)
			for (int b=0;b<=7;b++){
				if (dp[i][a][b]<-N||a>t[i]||b>t[i+1])
					continue;
				int v=dp[i][a][b];
				for (int x=0;x<=2;x++)
					for (int y=0;y<=2;y++)
						if (a+x*3+y<=t[i]&&b+y<=t[i+1])
							ckmax(dp[i+1][b+y][y],v+x+y);
			}
	int k=0;
	for (int i=0;i<=t[m];i++)
		ckmax(k,dp[m][i][0]+(t[m]-i)/3);
	cout<<ans+k;
	return 0;
}

  

 

posted @ 2019-02-08 12:52  zzd233  阅读(419)  评论(0编辑  收藏  举报