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; }
一点一点积累,一点一点蜕变!