原题链接:105.七夕祭
解题思路
分治+贪心+前缀和+中位数+排序
这道题有一个非常重要的性质就是,只会改变相邻的两个数的位置,因此我们交换两个数,只会改变一行或一列的喜爱小摊,而不会同时改变行和列的喜爱小摊,既然这样的话,我们就可以将这道题目分为两个部分,一部分是求行的最少次数,一部分是求列的最少次数。
既然如此的话,这道题目就成了环形的均分纸牌问题,均分纸牌是一道经典的贪心问题,以后会有专门的博客来讲解这个问题。但是环形均分纸牌问题和普通均分纸牌问题又有不同之处,因此我们要截取环为序列,所以说我们可以利用中位数把环形变为区间。
样例代码
//#define fre yes
#include <cstdio>
#include <algorithm>
const int N = 100005;
int H[N], Z[N], f[N];
// int hNumber, zNumber;
long long clac(int a[N], int n) {
long long ans = 0;
for (int i = 1; i <= n; i++) { //进行前缀和计算
a[i] -= a[0] / n;
f[i] = f[i - 1] + a[i];
}
std::sort(f + 1, f + 1 + n);
for (int i = 1; i <= n; i++) { //中位数技巧
ans += abs(f[i] - f[(n + 1) >> 1]);
} return ans;
}
int main() {
static int n, m, k;
scanf("%d %d %d", &n, &m, &k);
for (int i = 1, x, y; i <= k; i++) {
scanf("%d %d", &x, &y);
H[x]++, Z[y]++;
}
for (int i = 1; i <= n; i++) {
H[0] += H[i];
} for (int i = 1; i <= m; i++) {
Z[0] += Z[i];
} //想用变量存的,但是没想到后面不好处理
if(H[0] % n == 0 && Z[0] % m == 0) { //如果两个合法
printf("both %lld
", clac(H, n) + clac(Z, m));
} else if(H[0] % n == 0) { //如果单个合法
printf("row %lld
", clac(H, n));
} else if(Z[0] % m == 0) { //同上
printf("column %lld
", clac(Z, m));
} else puts("impossible");
return 0;
}
//作者:Nicoppa
//链接:https://www.acwing.com/solution/content/1236/
//来源:AcWing
//著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。