Codeforces Round #599 (Div. 1) C. Sum Balance 图论 dp

C. Sum Balance

Ujan has a lot of numbers in his boxes. He likes order and balance, so he decided to reorder the numbers.

There are 𝑘 boxes numbered from 1 to 𝑘. The 𝑖-th box contains 𝑛𝑖 integer numbers. The integers can be negative. All of the integers are distinct.

Ujan is lazy, so he will do the following reordering of the numbers exactly once. He will pick a single integer from each of the boxes, 𝑘 integers in total. Then he will insert the chosen numbers — one integer in each of the boxes, so that the number of integers in each box is the same as in the beginning. Note that he may also insert an integer he picked from a box back into the same box.

Ujan will be happy if the sum of the integers in each box is the same. Can he achieve this and make the boxes perfectly balanced, like all things should be?

Input

The first line contains a single integer 𝑘 (1≤𝑘≤15), the number of boxes.

The 𝑖-th of the next 𝑘 lines first contains a single integer 𝑛𝑖 (1≤𝑛𝑖≤5000), the number of integers in box 𝑖. Then the same line contains 𝑛𝑖 integers 𝑎𝑖,1,…,𝑎𝑖,𝑛𝑖 (|𝑎𝑖,𝑗|≤109), the integers in the 𝑖-th box.

It is guaranteed that all 𝑎𝑖,𝑗 are distinct.

Output

If Ujan cannot achieve his goal, output "No" in a single line. Otherwise in the first line output "Yes", and then output 𝑘 lines. The 𝑖-th of these lines should contain two integers 𝑐𝑖 and 𝑝𝑖. This means that Ujan should pick the integer 𝑐𝑖 from the 𝑖-th box and place it in the 𝑝𝑖-th box afterwards.

If there are multiple solutions, output any of those.

You can print each letter in any case (upper or lower).

Examples

input
4
3 1 7 4
2 3 2
2 8 5
1 10
output
Yes
7 2
2 3
5 1
10 4
input
2
2 3 -2
2 -1 5
output
No
input
2
2 -10 10
2 0 -20
output
Yes
-10 2
-20 1

Note

In the first sample, Ujan can put the number 7 in the 2nd box, the number 2 in the 3rd box, the number 5 in the 1st box and keep the number 10 in the same 4th box. Then the boxes will contain numbers {1,5,4}, {3,7}, {8,2} and {10}. The sum in each box then is equal to 10.

In the second sample, it is not possible to pick and redistribute the numbers in the required way.

In the third sample, one can swap the numbers −20 and −10, making the sum in each box equal to −10.

题意

现在有k个箱子,每个箱子里面有若干个数,每个箱子里面的每个数都一定是不同的。现在你需要从每个盒子里面拿出一个数,然后再放到任意一个箱子里面去,使得每个箱子里面的数的个数保持和之前一样,且所有箱子的和都一样。

问你是否存在这样的放法,且输出方案。

题解

所有数的和为sum的话,肯定sum%k==0才行。

假设最后的和为FinalSum,假设从箱子i里面拿出来的数为c[i],i箱子的数的和为sum[i],那么箱子i拿出c[i]之后所需要的数为 FinalSum - sum[i] + c[i]。

我们建一个图,我们令c[i]为x,FinalSum-sum[i]+c[i]为y。边就是从x连向y的有向边,表示我们从这个箱子里面拿出x,需要放回y;那么这个图建立起来,一定是若干个环,而且环没有公共边。

那么我们建立出若干个环之后,我们接下里的任务就是选择若干个环,使得这些环上面的点恰好为n个,且来自n个箱子。这个就是一个经典的dp了,枚举子集进行转移即可。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 16;
vector<pair<pair<int,long long>, int>> ans[1<<maxn];
map<long long,int> color;
vector<int>a[maxn];
long long sum[maxn];
int k;
int dp[1<<maxn];
int main(){
	scanf("%d",&k);
	long long all_sum = 0;
	for(int i=0;i<k;i++){
		int n;scanf("%d",&n);
		for(int j=0;j<n;j++){
			long long x;scanf("%lld",&x);
			a[i].push_back(x);
			all_sum+=x;
			sum[i]+=x;
			color[a[i][j]]=i;
		}
	}
	if(all_sum%k!=0){
		puts("NO");
		return 0;
	}
	all_sum/=k;
	for(int i=0;i<k;i++){
		for(int j=0;j<a[i].size();j++){
			long long cur = a[i][j];
			int used = 0;
			bool isOk = true;
			vector<pair<pair<int,long long>, int>> an;
			do{
				int cl = 0;
				auto it = color.find(cur);
				if(it!=color.end()){
					cl = it->second;
				}else{
					isOk = false;
					break;
				}
				if(used&(1<<cl)){
					isOk = false;
					break;
				}
				used|=(1<<cl);
				cur = cur + (all_sum - sum[cl]);
				auto cl2 = color.find(cur);
				if(cl2!=color.end()){
					an.push_back({{cl2->second,cur},cl});
				}
				// cout<<cur<<endl;
				// cout<<cur<<" "<<cl<<" "<<cl2<<endl;
			}while(cur!=a[i][j]);
			if(isOk){
				// cout<<"made! " << used << endl;
				dp[used]=1;
				ans[used] = std::move(an);
			}
			// cout<<"------"<<endl;
		}
	}

	for(int i=0;i<(1<<k);i++){
		if(dp[i])continue;
		for(int j=i;j>0;j=(j-1)&i){
			if(dp[j]&&dp[i&(~j)]){
				dp[i]=1;
				ans[i]=ans[j];
				for(auto&x : ans[i&(~j)]){
					ans[i].push_back(x);
				}
				break;
			}
		}
	}
	// for(int i=0;i<(1<<k);i++){
	//     cout<<dp[i]<<endl;
	// }
	int x = (1<<k) - 1;
	if (dp[x]) {
		cout<<"Yes"<<endl;
		sort(ans[x].begin(),ans[x].end());
		for(auto a:ans[x]){
			cout<<a.first.second<<" "<<a.second+1<<endl;;
		}
	} else {
		cout<<"No"<<endl;
	}
}
posted @ 2019-11-07 17:17  qscqesze  阅读(409)  评论(0编辑  收藏  举报