AcWing 105 七夕祭 (貪心)
阿新 • • 發佈:2020-11-04
題目連結: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\)
#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\n"); else if(!flagx && flagy){ printf("row %lld\n",ansx); }else if(flagx && !flagy){ printf("column %lld\n",ansy); }else{ printf("both %lld\n",ansx + ansy); } return 0; }