AcWing 105 七夕祭 (贪心)

题目链接: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;
}
原文地址:https://www.cnblogs.com/tuchen/p/13928624.html