1243E - Sum Balance(狀壓DP,圖)
題目鏈接:1243E - Sum Balance
題意:
給一個K,代表有K個箱子。第 個箱子有 個物品,價值分別爲。
現在分別從K個箱子中取精確的一個物品,並放回K個箱子(每個箱子精確放一個),要求最後的所有箱子內物品價值相等。並輸出方案。保證所有物品的價值唯一。
思路:
每個箱子取出一個物品,並放入一個箱子,若此刻我們將箱子看成頂點,取放的過程看作一條有向邊,那麼這裏有K個箱子K條邊,且每個頂點的出度和入度都等於1,顯然這個圖是由若干個簡單環組成的。
首先如果所有物品的總和不是 的倍數,那麼無解。否則我們將物品總和除以K,結果設爲 。
因爲保證所有的價值唯一,那麼我們將所有物品看作頂點,我們記錄第 個箱子的總和是 ,對於第 個箱子的物品 ,我們令 ,即將物品 a 拿走後,將物品 b 放到箱子 中可保證箱子總和爲 ,
此時若物品 b 存在且((物品b不在箱子 中)或(在箱子 中且是物品a ))。我們就讓 向 連接一條邊。
這樣我麼形成了一個圖,且每個頂點的出度爲1,現在我們給頂點覆上顏色,顏色爲所屬箱子,然後求出圖中所有的環,且環內顏色互不相同。
如果我們能組合出一組環,使得所有環的所有顏色互不相同且總顏色種類等於K,那麼這個就是答案。對於這一步,我們實現的時候可以使用狀壓DP,記錄每個環的狀態和每個環內的邊,然後狀壓DP即可,狀態轉移的時候用狀態的枚舉子集優化,時間複雜度可以達到。
記錄每個頂點的顏色可以使用map,這樣總體的時間複雜度爲
代碼:
#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;
}