ACwing 105.七夕祭

鏈接:https://www.acwing.com/problem/content/description/107/
題意:
一個n×mn\times m的二維矩陣,給出矩陣中的 tt 個點,點可以和相鄰的點交換,最後一個和第一個認爲是相鄰點,問時候可以滿足每行點個數一致,每列點個數一致,兩者都一致?
思路:
首先看看看 tt 能否被 n,mn,m整除,若都不能直接退出,然後討論行列的情況即可。

其次,行列互相不影響,可以分開去算,行或列都是 一個單獨的有環的"均分紙牌問題",枚舉斷點的位置 kk,設 aia_i 的前綴和爲 sis_i。如果從第 kk 個位置開始,那麼第 ii 堆和第 i+1i+1 堆交換的紙牌數就是 sisk|si-sk|
那麼總交換的牌數就是:
ans=i=1nsiskans = \displaystyle \sum^n_{i=1}{|s_i-s_k|}
找到總交換數最小的一次,就是答案。但是O(n2)O(n^2)的時間複雜度是過不了的。

所以需要優化:我們需要快速的找到 最優的點 kk,這就要用到一個經典 選址問題 的結論。
一條路上有 mm 戶人家,要在這條路上建一個學校,問建在哪裏可以使所有人家到達學校的總路程最小?
答:建在所有人家的中位數處可以使和最小。

先看兩點:
星星代表學校選址,綠色代表最優
學校建在兩點之間(包含端點)任意位置使可以和最小,因爲只要學校建在AB外的任何位置,和都會大於AB的長度(紅線),最小就是在AB內(綠線)。
在這裏插入圖片描述如何有偶數個人家,那麼學校建在最中間兩點之間(包含端點)就可以了。我們就可以選擇中間兩個端點的任一個,這個端點一定是所有人家的中位數。
在這裏插入圖片描述
而面對奇數情況,放在最中間的點上就行,因爲把中間的點去掉就退化成成偶數情況,可以在最中間兩點之間任放,此時當然是放在最中間的點上了,這樣可以使最中間的點到學校最短,否則就會多出一段最中間的點到學校的距離(紅線)。
在這裏插入圖片描述綜上中位數處是最優。
這樣我們也可以得到,sis_i的中位數處就是我們要選的最優點。
到此可以寫出代碼

#include <bits/stdc++.h>
#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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章