1. 程式人生 > 實用技巧 >Three Bags

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