UVa 11300 - Spreading the Wealth (数学/递推)

本文介绍了一种解决圆桌上每个人持有不等数量金币问题的方法,通过数学分析和算法设计,找到使所有人金币数相等时金币转移的最小值,涉及枚举、排序及中位数求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接


题目描述:

圆桌旁坐着n个人,每个人有一定数目的金币,金币总数能被n整除。每个人可以给相邻的人一些金币,最终使得每个人的金币数目相等,求被转手的金币数量的最小值。


输入第一行为整数n

接下来n行每行代表第i个人持有的金币数Ai


输出:被转手金币的最小值。


分析:首先每个人的最终状态我们是能确定的,因为最终金币相等,我们在读取时累计,读取结束后 m = sum / n 就是这个最终值。

令xi : 第i个人给了第i-1个人多少个金币(因为是一个圆桌,所以第1个人左边是第n个人,即当i = 1时 i-1 = n)

xi<0时相当于第i-1个人给第i个人金币

那么对每一个人来说: m = Ai - xi + x(i+1) 为什么不考虑i给i+1个人多少金币 因为这是可以相互转化的,比如1号给2号金币 然后2号又给1号金币 这样可以都转化为2号给1号金币

那么 x1 = Ai + x2 - m 

 x(i+1) = m - Ai + xi-1

         即 x2 = m - A2 + x1     = x1 - C1

      x3 = m - A2 + x2 = m-A3 +(m - A2 + x1) = 2*m - A3 - A2 +x1 = x1-C2

····

递推时发现每个xi都可以用一个常数+x1来代替

这样我们可以得到n个等式,但由于xn是可以由x1和xn-1得到的,所以我们实际上只得到了n-1个等式。

这样,我们换个角度,我们希望所有xi的绝对值之和尽量小,即|x1| + |x1 -C1| + |x-C2|+```|x1-Cn-1| 尽量小

把每一个C放到数轴上,我们要求的就是x1的位置,使得x1到所有C的距离之和最小

答案是: x1是所有C这个序列的中位数

实际上这是一个邮局选址问题:

证明过程

明确上面几步,我们可以开始求解

第一步 求出每个人最终手中有多少金币,答案就是总金币数/n

第二步 求出每个C

第三部 利用上面的证明结论求出结果


代码:

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
LL A[1000000 + 5];
LL C[1000000 + 5];
int main()
{
	int n;
	while(~scanf("%d",&n))
	{
		LL m = 0;
		LL s = 0;
		for(int i =1;i<=n;i++)
		{
			scanf("%lld",&A[i]);
			s += A[i];
		}
		m = s / n;
		C[0] = 0;
		for(int i = 1;i<n;i++)
		{
			C[i] = C[i-1] + A[i] - m;    //递推求出C 
		}
		sort(C,C+n);
		LL x = C[n/2]; 		//x1的选址 
		LL ans = 0;
		for(int i = 0;i<n;i++)
		{
			ans += abs(x - C[i]);  //求出距离 
		}
		printf("%lld\n",ans);
	}
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值