题目链接:https://www.acwing.com/problem/content/107/
我们发现:
交换左右相邻的摊点,只会改变某两列的数量,同理,交换上下相邻的摊点有改变某两行的数量
于是我们就可以将行和列分开考虑
而对于行和列:就是经典的均分纸牌问题,不过这里是环形均分纸牌
必然有一种最优解:有相邻两个位置不发生交换
假设有(T)张牌,(N)个人,
令 (A[i] = frac{T}{N}),(S[i])为前缀和
从第(k)个人把环断开,(S[i]) 的变化是都减掉 (S[k])
所以答案即为:$$sum_{i=1}^{N}mid S[i] - S[k] mid$$
这就是货仓选址问题,把(S)排序,取中位数即为(S[k])的最优解
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 100010;
int n, m, T;
int x[maxn] ,y[maxn], sum[maxn];
ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }
int main(){
n = read(), m = read(), T = read();
int X, Y;
for(int i=1;i<=T;++i){
X = read(), Y = read();
++x[X], ++y[Y];
}
int flagx = 0, flagy = 0;ll ansx = 0, ansy = 0;
// 行
if(T % n != 0) flagx = 1;
if(!flagx){
int num = T / n;
for(int i=1;i<=n;++i) x[i] -= num;
for(int i=1;i<=n;++i) sum[i] = sum[i-1] + x[i];
sort(sum + 1, sum + 1 + n);
int mid = sum[(n+1)/2];
for(int i=1;i<=n;++i) ansx += abs(sum[i] - mid);
}
memset(sum, 0, sizeof(sum));
// 列
if(T % m != 0) flagy = 1;
if(!flagy){
int num = T / m;
for(int i=1;i<=m;++i) y[i] -= num;
for(int i=1;i<=m;++i) sum[i] = sum[i-1] + y[i];
sort(sum + 1, sum + 1 + m);
int mid = sum[(m+1)/2];
for(int i=1;i<=m;++i) ansy += abs(sum[i] - mid);
}
if(flagx && flagy) printf("impossible
");
else if(!flagx && flagy){
printf("row %lld
",ansx);
}else if(flagx && !flagy){
printf("column %lld
",ansy);
}else{
printf("both %lld
",ansx + ansy);
}
return 0;
}