鏈接:https://www.acwing.com/problem/content/description/107/
題意:
一個的二維矩陣,給出矩陣中的 個點,點可以和相鄰的點交換,最後一個和第一個認爲是相鄰點,問時候可以滿足每行點個數一致,每列點個數一致,兩者都一致?
思路:
首先看看看 能否被 整除,若都不能直接退出,然後討論行列的情況即可。
其次,行列互相不影響,可以分開去算,行或列都是 一個單獨的有環的"均分紙牌問題",枚舉斷點的位置 ,設 的前綴和爲 。如果從第 個位置開始,那麼第 堆和第 堆交換的紙牌數就是 。
那麼總交換的牌數就是:
找到總交換數最小的一次,就是答案。但是的時間複雜度是過不了的。
所以需要優化:我們需要快速的找到 最優的點 ,這就要用到一個經典 選址問題 的結論。
一條路上有 戶人家,要在這條路上建一個學校,問建在哪裏可以使所有人家到達學校的總路程最小?
答:建在所有人家的中位數處可以使和最小。
先看兩點:
星星代表學校選址,綠色代表最優
學校建在兩點之間(包含端點)任意位置使可以和最小,因爲只要學校建在AB外的任何位置,和都會大於AB的長度(紅線),最小就是在AB內(綠線)。
如何有偶數個人家,那麼學校建在最中間兩點之間(包含端點)就可以了。我們就可以選擇中間兩個端點的任一個,這個端點一定是所有人家的中位數。
而面對奇數情況,放在最中間的點上就行,因爲把中間的點去掉就退化成成偶數情況,可以在最中間兩點之間任放,此時當然是放在最中間的點上了,這樣可以使最中間的點到學校最短,否則就會多出一段最中間的點到學校的距離(紅線)。
綜上中位數處是最優。
這樣我們也可以得到,的中位數處就是我們要選的最優點。
到此可以寫出代碼
#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);
}