UVa11300 Spreading the Wealth 题解

非常好的一道数学题。

原题链接(洛谷)

原题链接(UVa)

题目分析

(参考刘汝佳《算法竞赛入门经典 ⋅ \cdot 训练指南》)

本身看起来很复杂。不要急,我们慢慢分析。

首先,每个人最终的金币数是可以计算出来的,即总金币数去除以总人数,我们设这个数等于 M M M

假设一共有 n n n 个人,编号为 1 1 1 2 2 2 3 3 3 4 4 4 ⋯ \cdots 。设每个人刚开始拥有的金币数为 A i A_i Ai x i x_i xi 表示第 i i i 个人给其上一个人的金币数( x 1 x_1 x1 表示给 n n n 的金币数)。那么对于编号 1 1 1,经转手后的金币数就等于 M = A 1 − x 1 + x 2 M=A_1-x_1+x_2 M=A1x1+x2,同理: M = A 2 − x 2 + x 3 , M = A 3 − x 3 + x 4 ⋯ M=A_2-x_2+x_3,M=A_3-x_3+x_4\cdots M=A2x2+x3,M=A3x3+x4。最终我们就可以获得第 n n n 个式子 M = A n − x n + x 1 M=A_n-x_n+x_1 M=Anxn+x1,那我们是不是就可以解方程组了呢?很遗憾,最后一个方程是可以通过前 n − 1 n-1 n1 个方程推导出来的,故只有前 n − 1 n-1 n1 个方程是有用的。

尽管无法直接求解,我们还是可以用 x 1 x_1 x1 表示其他的 x i x_i xi,进而转化为单变量的极值问题。

对于第 1 1 1 个人,可以得到 x 2 = x 1 + M − A 1 x_2=x_1+M-A_1 x2=x1+MA1

对于第 2 2 2 个人,可以得到 x 3 = x 2 + M − A 2 = x 1 + M − A 2 + M − A 1 x_3=x_2+M-A_2=x_1+M-A_2+M-A_1 x3=x2+MA2=x1+MA2+MA1

对于第 3 3 3 个人,可以得到 x 4 = x 3 + M − A 3 = x 1 + M − A 3 + M − A 2 + M − A 1 x_4=x_3+M-A_3=x_1+M-A_3+M-A_2+M-A_1 x4=x3+MA3=x1+MA3+MA2+MA1

⋯ \cdots

所以对于第 n n n 个人,有 x n = x 1 + n M − ( ∑ i = 1 n A i ) x_n=x_1+nM-(\sum_{i=1}^nA_i) xn=x1+nM(i=1nAi)

最后,我们要求解的是转手金币数的最小值,也就是 min ⁡ { ∑ i = 1 n ∣ x i ∣ } \min\{\sum_{i=1}^n|x_i|\} min{i=1nxi},所以这个时候我们令 C i = ( ∑ i = 1 n A i ) − i ⋅ M C_i=(\sum_{i=1}^nA_i)-i\cdot M Ci=(i=1nAi)iM,这样,我们要求解的式子就变成 min ⁡ { ∣ x 1 ∣ + ∣ x 1 − C 1 ∣ + ∣ x 2 − C 2 ∣ + ⋯ + ∣ x n − C n ∣ } \min\{|x_1|+|x_1-C_1|+|x_2-C_2|+\cdots +|x_n-C_n|\} min{x1+x1C1+x2C2++xnCn}。由于两个值的差的绝对值的几何意义是二者的距离,所以我们把求最小值这个问题放在数轴上来解决,问题也转成求 x 1 x_1 x1 到各个 C i C_i Ci 的距离之和的最小值。

图解

假设我们的红圈( x 1 x_1 x1)向右微微移动了 d d d 个单位,那么对于左边就增加了 2 d 2d 2d 的距离,对于右边减少了 4 d 4d 4d 的距离,故减少了 2 d 2d 2d 的距离。所以每次只要我们向蓝圈更多的一边去移动,就会对答案产生贡献,进而推出当红圈两边的蓝圈数相等,即处于中位数时,可以获得最小值。之后我们将 C i C_i Ci 从小到大来排序,然后统计答案即可。

对于 A i A_i Ai,我们没有任何必要保存,具体实现请看代码:

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
ll M,sum;
int n;
ll C[1000005];

inline ll re(){
	register ll k=0,f=1ll;
	register char c=getchar();
	while(!isdigit(c)){
		if(c=='-') f=-1ll;
		c=getchar();
	}
	while(isdigit(c)){
		k=k*10ll+(c^48ll);
		c=getchar();
	}
	return 1ll*k*f;
}

void wr(ll x){
	if(x<0){
		x=~x+1;
		putchar('-');
	}
	if(x>9) wr(x/10ll);
	putchar(x%10ll^48ll);
}

signed main(){
	while(scanf("%d",&n)!=EOF){
		for(int i=1;i<=n;++i) C[i]=0;
		for(int i=1;i<=n;++i){
			ll x=re();
			C[i]=x+C[i-1];
		}
		M=C[n]/n;
		for(int i=1;i<=n;++i) C[i]-=i*M;
		sort(C+1,C+1+n);
		ll x=C[(n+1)/2],ans=0;
		for(int i=1;i<=n;++i)
			ans+=abs(x-C[i]);
		wr(ans);
		putchar('\n');
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值