ACwing 105.七夕祭

链接:https://www.acwing.com/problem/content/description/107/
题意:
一个 n × m n\times m n×m的二维矩阵,给出矩阵中的 t t t 个点,点可以和相邻的点交换,最后一个和第一个认为是相邻点,问时候可以满足每行点个数一致,每列点个数一致,两者都一致?
思路:
首先看看看 t t t 能否被 n , m n,m n,m整除,若都不能直接退出,然后讨论行列的情况即可。

其次,行列互相不影响,可以分开去算,行或列都是 一个单独的有环的"均分纸牌问题",枚举断点的位置 k k k,设 a i a_i ai 的前缀和为 s i s_i si。如果从第 k k k 个位置开始,那么第 i i i 堆和第 i + 1 i+1 i+1 堆交换的纸牌数就是 ∣ s i − s k ∣ |si-sk| sisk
那么总交换的牌数就是:
a n s = ∑ i = 1 n ∣ s i − s k ∣ ans = \displaystyle \sum^n_{i=1}{|s_i-s_k|} ans=i=1nsisk
找到总交换数最小的一次,就是答案。但是 O ( n 2 ) O(n^2) O(n2)的时间复杂度是过不了的。

所以需要优化:我们需要快速的找到 最优的点 k k k,这就要用到一个经典 选址问题 的结论。
一条路上有 m m m 户人家,要在这条路上建一个学校,问建在哪里可以使所有人家到达学校的总路程最小?
答:建在所有人家的中位数处可以使和最小。

先看两点:
星星代表学校选址,绿色代表最优
学校建在两点之间(包含端点)任意位置使可以和最小,因为只要学校建在AB外的任何位置,和都会大于AB的长度(红线),最小就是在AB内(绿线)。
ACwing 105.七夕祭_第1张图片如何有偶数个人家,那么学校建在最中间两点之间(包含端点)就可以了。我们就可以选择中间两个端点的任一个,这个端点一定是所有人家的中位数。
ACwing 105.七夕祭_第2张图片
而面对奇数情况,放在最中间的点上就行,因为把中间的点去掉就退化成成偶数情况,可以在最中间两点之间任放,此时当然是放在最中间的点上了,这样可以使最中间的点到学校最短,否则就会多出一段最中间的点到学校的距离(红线)。
ACwing 105.七夕祭_第3张图片综上中位数处是最优。
这样我们也可以得到, s i s_i si的中位数处就是我们要选的最优点。
到此可以写出代码

#include 
#define ll long long
using namespace std;

const int N = 1e5 + 7;
ll n,m,t,ans_r,ans_c,x,y,row,col,mid;
ll s[N];
ll cnt_c[N],cnt_r[N];
int main(){
    scanf("%lld%lld%lld",&n,&m,&t);
    for(int i = 1; i <= t; ++i){
        scanf("%lld %lld",&x,&y);
        cnt_r[x]++,cnt_c[y]++;
    }
    row = t / n, col = t / m;
    if(t % n && t % m){
        printf("impossible");
        return 0;
    }
    if(t % n == 0){
        for(int i = 1; i <= n; ++i){
            s[i] = cnt_r[i] - row + s[i-1];
        }
        sort(s+1,s+n+1);
        mid = s[n/2+1];
        for(int i = 1; i <= n; ++i){
            ans_r += abs(s[i] - mid);
            
        }
        if(t % m){
            printf("row %lld",ans_r);
            return 0;
        }
    }
    memset(s,0,sizeof s);
    if(t % m == 0){
        for(int i = 1; i<= m; ++i){
            s[i] = cnt_c[i] - col + s[i-1];
        }
        sort(s+1,s+m+1);
        mid = s[m/2+1];
        for(int i = 1; i <= m; ++i){
            ans_c += abs(s[i] - mid);
        }
        if( t % n){
            printf("column %lld",ans_c);
            return 0;
        }
    }
    printf("both %lld",ans_c+ans_r);
}

你可能感兴趣的:(Data_Structure)