zoukankan      html  css  js  c++  java
  • Codeforces 1110D. Jongmah 动态规划

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

    题意

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

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

      $n,mleq 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;
    }
    

      

  • 相关阅读:
    OSI安全体系结构
    PHP 二维数组根据相同的值进行合并
    Java实现 LeetCode 17 电话号码的字母组合
    Java实现 LeetCode 16 最接近的三数之和
    Java实现 LeetCode 16 最接近的三数之和
    Java实现 LeetCode 16 最接近的三数之和
    Java实现 LeetCode 15 三数之和
    Java实现 LeetCode 15 三数之和
    Java实现 LeetCode 15 三数之和
    Java实现 LeetCode 14 最长公共前缀
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/CF1110D.html
Copyright © 2011-2022 走看看