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

    总结

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

  • 相关阅读:
    SQLSERVER走起微信公众帐号已经开通搜狗微信搜索
    从0开始搭建SQL Server AlwaysOn 第三篇(配置AlwaysOn)
    从0开始搭建SQL Server AlwaysOn 第二篇(配置故障转移集群)
    从0开始搭建SQL Server AlwaysOn 第一篇(配置域控)
    恢复SQL Server被误删除的数据(再扩展)
    Windows server 2012 添加中文语言包(英文转为中文)(离线)
    SQL Server技术内幕笔记合集
    将表里的数据批量生成INSERT语句的存储过程 增强版
    在SQL2008查找某数据库中的列是否存在某个值
    SQLSERVER走起 APP隆重推出
  • 原文地址:https://www.cnblogs.com/jz-597/p/11579499.html
Copyright © 2020-2023  润新知