• 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;
    }
    

      

  • 相关阅读:
    Oracle Relink RAC Binary Options 说明
    Oracle 10g 对象 默认 ITL 数量 测试
    Oracle 相关的专业术语 说明
    Oracle 11g 新特性 自适应游标共享(Adaptive Cursor Sharing: ACS) 说明
    symbian 学习笔记(1) 基础
    symbian 学习笔记(3) 手机独有
    计算机体系结构几个常用的知识点记录
    数据结构和算法笔记(3)
    windows mobile 通用曾抽象
    一些简单常用算法整理学习
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/CF1110D.html
Copyright © 2020-2023  润新知