题:D. Same Count One
题意:给出
n
n
n个长度为
m
m
m并且由
0
0
0和
1
1
1组成的数组,可以进行的操作为任意选择两个数组,并且两个数组中选择相同的位置进行交换,问最少交换多少次可以让所有数组中
1
1
1的个数都相同。
参考文章:点我
一些想法:其实对于不在正确位置上的坏元素,最终都是要换掉的。可以这样想,现在把每个数组中的坏元素拿出来然后放在一起,有一个神秘人物暗中操作一下,所有数组中的元素就都是正确的了。这其实可以用环(?)(也许吧,反正我一直是这么想的)来理解,
1
1
1需要换到
4
4
4位置上,
2
2
2需要换到
1
1
1位置上,
3
3
3需要换到
2
2
2位置上,
4
4
4需要换到
3
3
3位置上,求最小交换次数。这里面,
1
、
2
、
3
、
4
1、2、3、4
1、2、3、4都需要出来,那就先让
1
1
1和
4
4
4换,
4
4
4和
3
3
3换,
3
3
3和
2
2
2换,
2
2
2再和
1
1
1换。形成的环就是
1
→
4
→
3
→
2
→
1
1\rightarrow4\rightarrow3\rightarrow2\rightarrow1
1→4→3→2→1,
1
1
1首尾成环,并且是最优的。毕竟最小就需要换这些,那只要这些能满足就是最优的了。
解法:那么对于这一题来说,反正
1
1
1多的要和
1
1
1少的换,那就把
1
1
1的个数排序,然后首尾指两个指针,依次交换。这里要让超过平均值,和没有超过平均值的换。并且只要有一个换到平均值,就不用再换了。这样做是否一定可行?或者一定能找到可以交换的列?先设
1
1
1多的行(超过平均值的)为
q
q
q,
1
1
1少的为
p
p
p。最不好的情况就是在
p
p
p由
1
1
1的位置,
q
q
q全都有,但是
q
q
q其他位置的
1
1
1是可以换给
p
p
p的。
后记(小声
b
b
bb
bb)这个题的范围是
[
1
,
n
]
[1,n]
[1,n],用
[
0
,
n
)
[0,n)
[0,n)可能会有奇怪的问题(重写一下就行了)
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int length = 1e5 + 5;
bool comp(pair<int, int> a, pair<int, int> b)
{
return a.second < b.second;
}
int main(void)
{
int t;
scanf_s("%d", &t);
for (int i = 0; i < t; i++)
{
int n, m;
scanf_s("%d%d", &n, &m);
vector<vector<int>> graph(n + 5);
vector<pair<int, int>> res;
int sum = 0;
for (int i = 1; i <= n; i++)
{
vector<int> tmp(m + 5);
graph[i]=tmp;
int yh = 0;
for (int j = 1; j <= m; j++)
{
scanf_s("%d", &graph[i][j]);
if (graph[i][j] == 1)
{
yh++;
}
}
res.push_back({ i,yh });
sum += yh;
}
if (sum%n)
{
printf("-1\n");
continue;
}
sort(res.begin(), res.end(), comp);
int k = sum / n;
int l = 0;
int r = n-1;
vector<pair<pair<int, int>, int>> edge;
while (l < r)
{
int i = 1;
while (res[l].second<k&&i <= m && res[r].second>k)
{
int idl = res[l].first;
int idr = res[r].first;
if (graph[idl][i] == 0 && graph[idr][i] == 1)
{
swap(graph[idl][i], graph[idr][i]);
res[l].second++;
res[r].second--;
edge.push_back({ {idl,idr},i });
}
i++;
}
if (res[l].second == k)l++;
if (res[r].second == k)r--;
}
printf("%d\n", edge.size());
for (auto u : edge)
printf("%d %d %d\n", u.first.first, u.first.second, u.second);
}
}