题目大意:
有 k k k个箱子,箱子 i i i有 n i n_i ni个数字,能否满足:
在每个箱子取一个数字,一共取出k个数字。
再把这k个数字重新分配给每个箱子,使得每个箱子的数字总和相等。
如果不能满足,输出no,如果可以满足,输出yes并打印方案(每个箱子取出哪个数字,放到哪个箱子)。
( k ≤ 15 , n ≤ 5000 , ∣ a i ∣ ≤ 1 e 9 ) (k\le 15,n \le 5000, |a_i| \le 1e9) (k≤15,n≤5000,∣ai∣≤1e9)保证所有数字两两不同
解题思路:
容易知道,如果可以满足,那么最终每个箱子的总和为 s u m / k sum/k sum/k,其中 s u m = ∑ a i sum = \sum a_i sum=∑ai.那么对于每个箱子的数字,可以直接算出如果从这个箱子取出这个数字,那么应该再放入的数字的值。那么可以建一个有向图:数字 a i a_i ai如果取出,算出来需要放入 a j a_j aj。那么加有向边 a i − > a j a_i->a_j ai−>aj。这样如果找到一个环的集合,使得覆盖每个容器恰好一次,那么就可以构建出答案。
因为每个点只有一个出度,所以可以直接枚举起点找环。找到一个覆盖部分容器刚好一次的环,就记录下来。这样我们得到了所有覆盖某些容器恰好一次的环。
然后我们利用子集DP获取恰好覆盖所有容器的环的集合。复杂度 O ( 3 k ) O(3^k) O(3k)
#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;
}