Problem - D - Codeforces
ChthollyNotaSeniorious收到了AquaMoon的一份特殊礼物:n个长度为m的二进制数组。AquaMoon告诉他,在一次操作中,他可以选择任何两个数组和1到m中的任何位置,并交换这些数组中位置的元素。
他对这个游戏很着迷,他想找到使所有数组中的1的数量相同所需的最少操作数。他邀请你参与这个有趣的游戏,所以请你尝试找到它
如果可以的话,请按照输出部分描述的格式输出具体的交换步骤。否则,请输出-1。
输入
输入的第一行包含一个整数t(1≤t≤2⋅104)--测试案例的数量。测试用例的描述如下。
每个测试用例的第一行包含两个整数n和m(2≤n≤105,2≤m≤105)。
下面n行中的第i行包含m个整数ai,1,ai,2,...,ai,m(0≤ai,j≤1)--第i个数组的元素。
保证所有测试案例的n⋅m之和不超过106。
输出
对于每个测试案例,如果目标无法实现,则输出-1。
否则,在第一行输出k (0≤k≤mn) - 所需的最小操作数。
下面k行中的第i行应该包含3个整数,xi,yi,zi (1≤xi,yi≤n,1≤zi≤m),它们描述了交换axi,zi,ayi,zi的操作:交换第xi和yi个数组的第zi个数字。
例子
inputCopy
3
3 4
1 1 1 0
0 0 1 0
1 0 0 1
4 3
1 0 0
0 1 1
0 0 1
0 0 0
2 2
0 0
0 1
输出拷贝
1
2 1 1
1
4 2 2
-1
注意
在第一个测试案例中,只需做一个操作:交换第二行和第一行的第一个元素。数组将变成[0,1,1,0],[1,0,1,0],[1,0,0,1],每个数组正好包含两个1。
题解:
写题时一直在想一行行遍历,想的很麻烦
结束后想想,其实要先遍历列,因为是每行之间相同的列进行交换
我们一列列遍历,每次把行1数量大于平均值的存起来且a[j][i]等于1的行存起来
把行数量小于1的数量小于平均值的且a[j][i]等于0的行存起来
不断匹配,这样一定是最优的,能匹配就匹配
#include<iostream>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<map>
#include<cstring>
#include<cmath>
using namespace std;
#define int long long
struct node
{
int x,y,z;
};
void solve()
{
int n,m;
cin >> n >> m;
vector<int> a[n+1];
map<int,int> f;
int cnt = 0;
for(int i = 1;i <= n;i++)
{
a[i].resize(m+1);
for(int j = 1;j <= m;j++)
{
cin >> a[i][j];
if(a[i][j])
{
f[i] ++;
cnt++;
}
}
}
if(cnt % n)
{
cout << "-1\n";
return ;
}
vector<node> ans;
cnt = cnt/n;
for(int i = 1;i <= m;i++)
{
queue<int> q1,q2;
for(int j = 1;j <= n;j++)
{
if(f[j] > cnt&&a[j][i])
{
q1.push(j);
}
if(f[j] < cnt&&!a[j][i])
{
q2.push(j);
}
}
while(q1.size()&&q2.size())
{
int x = q1.front();
int y = q2.front();
q1.pop();
q2.pop();
f[x]--;
f[y]++;
ans.push_back({x,y,i});
}
}
cout<<ans.size()<<"\n";
for(auto [x,y,z]:ans)
{
cout << x<<" "<<y<<" "<<z<<"\n";
}
}
//dabcdabbddd
//dabcdabbddd
//ddadd
signed main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
int t = 1;
cin >> t;
while(t--)
{
solve();
}
}