105.七夕祭

原题链接: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\n", clac(H, n) + clac(Z, m));
    } else if(H[0] % n == 0) { //如果单个合法
        printf("row %lld\n", clac(H, n));
    } else if(Z[0] % m == 0) { //同上
        printf("column %lld\n", clac(Z, m));
    } else puts("impossible");
    return 0;
}

//作者:Nicoppa
//链接:https://www.acwing.com/solution/content/1236/
//来源:AcWing
//著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
posted @ 2021-01-14 18:16  hnkjdx_react  阅读(96)  评论(0编辑  收藏  举报