題目大意:
有個箱子,箱子有個數字,能否滿足:
在每個箱子取一個數字,一共取出k個數字。
再把這k個數字重新分配給每個箱子,使得每個箱子的數字總和相等。
如果不能滿足,輸出no,如果可以滿足,輸出yes並打印方案(每個箱子取出哪個數字,放到哪個箱子)。
保證所有數字兩兩不同
解題思路:
容易知道,如果可以滿足,那麼最終每個箱子的總和爲,其中.那麼對於每個箱子的數字,可以直接算出如果從這個箱子取出這個數字,那麼應該再放入的數字的值。那麼可以建一個有向圖:數字如果取出,算出來需要放入。那麼加有向邊。這樣如果找到一個環的集合,使得覆蓋每個容器恰好一次,那麼就可以構建出答案。
因爲每個點只有一個出度,所以可以直接枚舉起點找環。找到一個覆蓋部分容器剛好一次的環,就記錄下來。這樣我們得到了所有覆蓋某些容器恰好一次的環。
然後我們利用子集DP獲取恰好覆蓋所有容器的環的集合。複雜度
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 3e5 + 50;
const int inf = 0x3f3f3f3f;
map<int, int> mp;
struct node{
vector<int> ned;
}q[maxn];
int tot = 0, n, a[maxn], cnt = 0;
ll sum;
ll D[16];
ll to(int x){
int id = mp[x];
return sum - (D[id]-x);
}
int dp[1<<16];
vector<int> path;
void dfs(ll x, int mask){
if(x > inf) return;
if(mp.find(x) == mp.end()) return;
int id = mp[x];
if((mask>>id)&1){//出現過
int st = -1;
for(int i = 0; i < path.size(); ++i){
if(path[i] == x) st = i;
}
if(st == -1) return;//無環
for(int i = 0; i < st; ++i) mask ^= (1<<mp[path[i]]);
if(dp[mask]) return;//此環出現過
++tot;
dp[mask] = -tot;
for(int i = st; i < path.size(); ++i){
q[tot].ned.push_back(path[i]);
}
return;
}
path.push_back(x);
dfs(to(x), mask|(1<<id));
}
vector<int> ans;
void go(int mask){
if(dp[mask] < 0){
int id = -dp[mask];
for(int i = 0; i < q[id].ned.size(); ++i){
ans.push_back(q[id].ned[i]);
}return;
}
go(dp[mask]); go(mask^dp[mask]);
}
int main()
{
cin>>n;
sum = 0;
for(int i = 0; i < n; ++i){
int k; scanf("%d", &k);
while(k--){
scanf("%d", &a[cnt]);
mp[a[cnt]] = i;
sum += a[cnt];
D[i] += a[cnt];
cnt++;
}
}
if(sum%n != 0){
printf("No\n"); return 0;
}
sum /= n;
for(int i = 0; i < cnt; ++i){
path.clear();
dfs(a[i], 0);
}
for(int mask = 1; mask < (1<<n); ++mask){
if(dp[mask]) continue;
for(int sub = (mask-1)&mask; sub; sub = (sub-1)&mask){
if(dp[sub] && dp[mask^sub]){
dp[mask] = sub;
break;
}
}
}
if(!dp[(1<<n)-1]) cout<<"No"<<endl;
else{
cout<<"Yes"<<endl;
go((1<<n)-1);
int pre[15], val[15];
for(int i = 0; i < ans.size(); ++i){
int x = ans[i];
int id = mp[x];
int v = to(x);
val[id] = x;
pre[mp[v]] = id;
}
for(int i = 0; i < n; ++i){
cout<<val[i]<<" "<<pre[i]+1<<endl;
}
}
//for(int i = 0; i < (1<<n); ++i) cout<<"i:"<<i<<" dp:"<<dp[i]<<endl;
}