https://www.acwing.com/problem/content/107/
很显然行和列是独立的,求出平均值之后变成“一个环,交换最小的次数使得各个位置相等”。假设要交换,则考虑最大的数,它要么把货物传递到左边,要么把货物传递到右边。多出来的部分一直累过去,假如多出来的部分是负的则会欠债,而且这个相差的值是会每次累计到总成本里面的。
这样做是错的,因为不能保证最大的数就一定是边界,也就是最大的数不一定不从其他地方接收牌。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m, t;
int x[200005];
int y[200005];
ll calc_c() {
int maxid = 0;
for(int j = 1; j <= m; ++j) {
if(y[j] > y[maxid])
maxid = j;
}
int mean = t / m;
ll sum = 0, cur = 0;
for(int j = maxid, k = 1; k <= m; ++j, ++k) {
cur += y[j] - mean;
sum += abs(cur);
}
ll tsum = sum;
sum = 0, cur = 0;
for(int j = maxid + m, k = 1; k <= m; --j, ++k) {
cur += y[j] - mean;
sum += abs(cur);
}
return min(sum, tsum);
}
ll calc_r() {
int maxid = 0;
for(int i = 1; i <= n; ++i) {
if(x[i] > x[maxid])
maxid = i;
}
int mean = t / n;
ll sum = 0, cur = 0;
for(int i = maxid, k = 1; k <= n; ++i, ++k) {
cur += x[i] - mean;
sum += abs(cur);
}
ll tsum = sum;
sum = 0, cur = 0;
for(int i = maxid + n, k = 1; k <= n; --i, ++k) {
cur += x[i] - mean;
sum += abs(cur);
}
return min(sum, tsum);
}
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
scanf("%d%d%d", &n, &m, &t);
for(int ti = 1; ti <= t; ++ti) {
int u, v;
scanf("%d%d", &u, &v);
x[u]++, x[u + n]++;
y[v]++, y[v + m]++;
}
if((t % n) && (t % m))
printf("impossible");
else {
ll sum = 0;
if(t % n) {
printf("column");
sum = calc_c();
} else if(t % m) {
printf("row");
sum = calc_r();
} else {
printf("both");
sum = calc_r() + calc_c();
}
printf(" %lld
", sum);
}
}
正确的做法把行列分开看,问题变成了“糖果传递”。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
int a[2000005];
ll s[2000005];
ll calc() {
ll sum = 0;
for(int i = 1; i <= n; ++i)
sum += a[i];
int mean = sum / n;
for(int i = 1; i <= n; ++i) {
s[i] = s[i - 1] + a[i] - mean;
}
sort(s + 1, s + 1 + n);
ll res = 0;
int m = s[(n + 1) >> 1];
for(int i = 1; i <= n; ++i)
res += abs(s[i] - m);
return res;
}
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
printf("%lld
", calc());
}
改成这样:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m, t;
int x[2000005];
int y[2000005];
ll s[2000005];
ll calc_r() {
int mean = t / n;
for(int i = 1; i <= n; ++i)
s[i] = s[i - 1] + x[i] - mean;
sort(s + 1, s + 1 + n);
ll res = 0;
ll mid = s[(n + 1) >> 1];
for(int i = 1; i <= n; ++i)
res += abs(s[i] - mid);
return res;
}
ll calc_c() {
int mean = t / m;
for(int j = 1; j <= m; ++j)
s[j] = s[j - 1] + y[j] - mean;
sort(s + 1, s + 1 + m);
ll res = 0;
ll mid = s[(m + 1) >> 1];
for(int j = 1; j <= m; ++j)
res += abs(s[j] - mid);
return res;
}
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
scanf("%d%d%d", &n, &m, &t);
for(int ti = 1; ti <= t; ++ti) {
int u, v;
scanf("%d%d", &u, &v);
x[u]++, x[u + n]++;
y[v]++, y[v + m]++;
}
if((t % n) && (t % m))
printf("impossible");
else {
ll sum = 0;
if(t % n) {
printf("column");
sum = calc_c();
} else if(t % m) {
printf("row");
sum = calc_r();
} else {
printf("both");
sum = calc_r() + calc_c();
}
printf(" %lld
", sum);
}
}
PS:行和列写反都可以过5个点???