zoukankan      html  css  js  c++  java
  • 5432. 【NOIP2017提高A组集训10.28】三元组

    题目

    题目大意

    给你(X+Y+Z)个三元组((x_i,y_i,z_i))
    然后选(X)(x_i),选(Y)(y_i),选(Z)(z_i)
    每个三元组只能选择其中一个。
    问最大的和。


    思考历程

    想不到贪心……
    于是只能(DP)了……
    (DP)就不用说了吧……


    正解

    首先考虑(X=0)的情况:
    按照(z-y)排个序,前面(Z)个选择(z),后面(Y)个选择(y)
    这就是一个可撤销贪心的思路,可以看成先全部选(y),然后选(Z)(z-y)最大的。

    然后就是普通的情况。首先强制所有选(x),然后按照(z-y)排个序。
    枚举(z-y)的分界点,在前面的选(z)(x),在后面的选(y)(x)
    那么就变成了上面的问题:在(x)(z)中选择,显然是(z)(z-x)最大的(Z)个。
    这个东西可以用数据结构维护,只不过会TLE。
    于是可以搞个桶,用一个指针(l)表示当前选的最小的数在桶中的位置。
    新加进来一个树的时候,用它在同种的位置和(l)比较一下,如果更大,说明(l)废了,于是就将它加进桶中,然后(l)往后找下一个最小的。
    很显然,随着分界点朝右延伸,(l)一定是越来越大。

    右边的同理。
    如果排序也用桶排序,那就可以达到真正的(O(n))


    代码

    然而我懒得打桶排序,就直接用自带的快排过去了……

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 1500010
    #define ll long long
    inline int input(){
    	char ch=getchar();
    	while (ch<'0' || '9'<ch)
    		ch=getchar();
    	int x=0;
    	do{
    		x=x*10+ch-'0';
    		ch=getchar();
    	}
    	while ('0'<=ch && ch<='9');
    	return x;
    }
    int n,X,Y,Z;
    struct Triple{
    	int x,y,z;
    } _t[N],t[N];
    int q[N],r[N];
    int c[N];
    inline bool cmp1(int a,int b){return _t[a].z-_t[a].y>_t[b].z-_t[b].y;}
    inline bool cmp2(int a,int b){return c[a]<c[b];}
    bool used[N];
    ll ans1[N],ans2[N];
    inline void work(int Z,int Y,ll *ans){
    	memset(used,0,sizeof(int)*(n+1));
    	for (int i=1;i<=n;++i)
    		q[i]=i;
    	sort(q+1,q+n+1,cmp2);
    	for (int i=1;i<=n;++i)
    		r[q[i]]=i;
    	ll sum=0;
    	int l=n+1;
    	for (int i=1;i<=Z;++i){
    		sum+=c[i];
    		l=min(l,r[i]);
    		used[r[i]]=1;
    	}
    	for (int i=Z;i<n-Y+1;++i){
    		ans[i]=sum;
    		if (l<r[i+1]){
    			sum+=c[i+1]-c[q[l]];
    			used[l]=0;
    			used[r[i+1]]=1;
    			while (!used[l])
    				++l;
    		}
    	}
    }
    int main(){
    	freopen("triple.in","r",stdin);
    	freopen("triple.out","w",stdout);
    	X=input(),Y=input(),Z=input();
    	n=X+Y+Z;
    	ll sumx=0;
    	for (int i=1;i<=n;++i)
    		_t[i]={input(),input(),input()},sumx+=_t[i].x;
    	for (int i=1;i<=n;++i)
    		q[i]=i;
    	sort(q+1,q+n+1,cmp1);
    	for (int i=1;i<=n;++i)
    		t[i]=_t[q[i]];
    	for (int i=1;i<=n;++i)
    		c[i]=t[i].z-t[i].x;
    	work(Z,Y,ans1);
    	reverse(t+1,t+n+1);
    	for (int i=1;i<=n;++i)
    		c[i]=t[i].y-t[i].x;
    	work(Y,Z,ans2);
    	ll res=0;
    	for (int i=Z;i<n-Y+1;++i)
    		res=max(res,sumx+ans1[i]+ans2[n-i]);
    	printf("%lld
    ",res);
    	return 0;
    }
    

    总结

    贪心要靠大胆地猜想……
    有时候可以通过强制、分界点、可撤销贪心的方式转化成一个更加简单的问题。

  • 相关阅读:
    Android Gradle Plugin指南(五)——Build Variants(构建变种版本号)
    文件内容操作篇clearerr fclose fdopen feof fflush fgetc fgets fileno fopen fputc fputs fread freopen fseek ftell fwrite getc getchar gets
    文件操作篇 close creat dup dup2 fcntl flock fsync lseek mkstemp open read sync write
    嵌入式linux应用程序调试方法
    version control system:git/hg/subversion/cvs/clearcase/vss。software configruation management。代码集成CI:Cruisecontrol/hudson/buildbot
    最值得你所关注的10个C语言开源项目
    如何记录linux终端下的操作日志
    CentOS 5.5 虚拟机安装 VirtualBox 客户端增强功能
    sizeof, strlen区别
    C/C++嵌入式开发面试题
  • 原文地址:https://www.cnblogs.com/jz-597/p/11579499.html
Copyright © 2011-2022 走看看