Three Bags

传送门:http://codeforces.com/contest/1467/problem/C

题意

  给你三个背包(集合),分别有若干个数字,你可以进行任意次操作,每次选出两个不同背包各一个数字a和b,然后把b移除,把a的值变成a-b。要求在若干次操作之后,三个背包剩下唯一一个值,使该值最大。

思路前提

  我们来看最简单的一种情形,三个集合都只有一个数,分别是x,y,z。

  假定y是最终的那个数字,如果想让x做正贡献,就进行一次操作z变z-x,把x消去,然后让y变y-(z-x),把z-x消去。反之x为负贡献,z为正贡献。

  一般的,我们可以知道,若干个数可以通过一个数作为中转站,正贡献到最终答案,而那个中转站必须为负贡献。

具体思路

  设x,y,z分别是A,B,C三个集合中的三个数字,设y是最终贡献的那个数,在任何情况下,我们总能找到下面这种情况:

  

  该操作将:

    AB集合除x和y的其他元素,通过中转站z正贡献到y里面(当然B集合里面的元素也可以由x中转)。

    将C集合除z的其他元素,通过中转站x贡献到y里面。

  这样除去x和z为负贡献,其余数均为正贡献。那么是否最优解就是选两个集合,让其中的最小值贡献分别为负?

  还有一种情况,也是样例1里面的情况,让B集合除y的元素和整个C集合通过中转站x贡献到y里,再让整个A集合负贡献到y里。

  

  也就是说牺牲一个集合,让其余两个集合全为正贡献。

  所以整个思路已经清晰,在上两种方案里取max即可。

AC代码

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 3e5+5;
int n1,n2,n3;
ll a[maxn],b[maxn],c[maxn];
ll ans=-1;
ll s[4];
ll m[4];
int main()
{
    cin>>n1>>n2>>n3;
    m[1]=m[2]=m[3]=1e18;
    for(int i=1;i<=n1;i++){
        cin>>a[i];
        s[1]+=a[i];
        m[1]=min(m[1],a[i]);
    } 
    for(int i=1;i<=n2;i++){
        cin>>b[i];
        s[2]+=b[i];
        m[2]=min(m[2],b[i]);
    } 
    for(int i=1;i<=n3;i++){
        cin>>c[i];
        s[3]+=c[i];
        m[3]=min(m[3],c[i]);
    } 
    ll sum=s[1]+s[2]+s[3],minn=m[1]+m[2]+m[3]; 
    
    for(int i=1;i<=3;i++){
        ans=max(ans,sum-s[i]*2);
    }
    
    for(int i=1;i<=3;i++){
        ans=max(ans,sum-(minn-m[i])*2);
    }
    cout<<ans;
    return 0;
} 

 

 

 

  

 

 

 

  

posted @ 2021-01-12 22:58  艾尔夏尔-Layton  阅读(138)  评论(0编辑  收藏  举报