1243E - Sum Balance(狀壓DP,圖)

1243E - Sum Balance(狀壓DP,圖)

題目鏈接:1243E - Sum Balance
題意:

給一個K,代表有K個箱子。第 ii 個箱子有 nin_i 個物品,價值分別爲ai,1,ai,2...ai,nia_{i,1},a_{i,2}...a_{i,n_i}

現在分別從K個箱子中取精確的一個物品,並放回K個箱子(每個箱子精確放一個),要求最後的所有箱子內物品價值相等。並輸出方案。保證所有物品的價值唯一

k[1,15],ni[1,5000],ai,j[1e9,1e9]k\in[1,15],n_i\in[1,5000],a_{i,j}\in[-1e9,1e9]

思路:

每個箱子取出一個物品,並放入一個箱子,若此刻我們將箱子看成頂點,取放的過程看作一條有向邊,那麼這裏有K個箱子K條邊,且每個頂點的出度和入度都等於1,顯然這個圖是由若干個簡單環組成的。

首先如果所有物品的總和不是 KK 的倍數,那麼無解。否則我們將物品總和除以K,結果設爲 aveave

因爲保證所有的價值唯一,那麼我們將所有物品看作頂點,我們記錄第 ii 個箱子的總和是 sum[i]sum[i] ,對於第 ii 個箱子的物品 aa ,我們令 b=ave(sum[i]a)b=ave-(sum[i]-a),即將物品 a 拿走後,將物品 b 放到箱子 ii 中可保證箱子總和爲 aveave

此時若物品 b 存在且((物品b不在箱子 ii)或(在箱子 ii 中且是物品a ))。我們就讓 aabb 連接一條邊。

這樣我麼形成了一個圖,且每個頂點的出度爲1,現在我們給頂點覆上顏色,顏色爲所屬箱子,然後求出圖中所有的環,且環內顏色互不相同。

如果我們能組合出一組環,使得所有環的所有顏色互不相同且總顏色種類等於K,那麼這個就是答案。對於這一步,我們實現的時候可以使用狀壓DP,記錄每個環的狀態和每個環內的邊,然後狀壓DP即可,狀態轉移的時候用狀態的枚舉子集優化,時間複雜度可以達到3k3^k

記錄每個頂點的顏色可以使用map,這樣總體的時間複雜度爲O(kalln+allnlog(alln))+3kO(k*alln+alln*log(alln))+3^k

代碼:

#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int N=18;
vector<ll> box[N];
ll sum[N];
unordered_map<ll,ll> color;
int dp[1<<N];
vector<P> take[1<<N];
unordered_map<ll,ll> to;
/*
記錄每個點的顏色,以及箱子的和
1.建圖
2.找環 並記錄過程
3.dp並記錄過程
*/
P ans[N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin>>n;
    ll allsum=0;
    for(int i=0; i<n; ++i)
    {
        ll k;
        cin>>k;
        box[i].resize(k);
        sum[i]=0;
        for(int j=0; j<k; ++j)
        {
            cin>>box[i][j];
            allsum+=box[i][j];
            sum[i]+=box[i][j];
            color[box[i][j]]=i;
        }
    }
    if(allsum%n!=0)
    {
        cout<<"No"<<'\n';
        return 0;
    }
    allsum/=n;
    for(int i=0; i<n; ++i)
    {
        for(ll v:box[i])
        {
            ll need=allsum-(sum[i]-v);
            auto it=color.find(need);
            if(it!=color.end())
            {
                ll w=it->second;
                if(w!=i||need==v)
                    to[v]=need;
            }
        }
    }
    for(int i=0; i < n; ++i)
    {
        for(ll v:box[i])
        {
            int S=0;
            bool isok=true;
            ll cur=v;
            vector<P> place;
            do
            {
                if( (S&(1<<color[cur])) !=0){
                    isok=false;
                    break;
                }
                S|=1<<color[cur];
                auto it=to.find(cur);
                if(it!=to.end())
                {
                    place.push_back(make_pair(it->second,color[cur]));//什麼物品放到哪個箱子
                    cur=it->second;
                }
                else
                {
                    isok=false;
                    break;
                }

            }
            while(cur!=v);
            if(isok==true)
            {
                dp[S]=1;
                take[S]=place;
            }
        }
    }
    for(int i=0;i<(1<<n);++i){
        if(dp[i])
            continue;
        for(int sub=i;sub!=0;sub=(sub-1)&i)
        {
            int o=i^sub;
            if(dp[sub]&&dp[o])
            {
                dp[i]=1;
                take[i]=take[sub];
                take[i].insert(take[i].end(),take[o].begin(),take[o].end());
                break;
            }
        }
    }
    if(!dp[(1<<n)-1]){
        cout<<"No"<<'\n';
        return 0;
    }
    cout<<"Yes"<<'\n';
    for(P &p:take[(1<<n)-1]){
        ll val=p.first, id=p.second;
        ans[color[val]]=make_pair(val,id);
    }
    for(int i=0;i<n;++i)
        cout<<ans[i].first<<" "<<ans[i].second+1<<endl;
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章