UVa 11300 - Spreading the Wealth

本文解决了一个关于一群人围成一圈分配硬币的问题,目标是通过调整硬币数量使得传递的硬币总数最少。采用数学方法,将问题转化为求解最优Z[i]值,最终证明了中位数为最优化解。

題目:有一群人圍城一個圈,每個人有一些硬幣,他們每個人可以給兩側的人分一些硬幣,

            假定硬幣可以均分,求分時需要最少的移動硬幣數量。

分析:數學題,中位數。

            設最後每個人的硬幣數量是K個,即平均數;

            設第i個人有C[i]个硬币,給左側、右側的人分別L[i]、R[i]個硬幣;

            最後第i個人最後的硬幣數量為:R[i-1]-L[i] + C[i] - R[i] + L[i] = K;

            設Z[i] = L[i] - R[i-1],即每次都看成是給左側的人硬幣(個右側則Z[i]為負數);

            上式可以寫成:- Z[i] + C[i] + Z[i+1] = K;

            整理得:Z[i+1] = Z[i] + K - C[i];

            用Z[1]表示其他的Z[i]得到:

                     Z[2] = Z[1] + K - C[1];

                     Z[3] = Z[2] + K - C[2] = Z[1] + 2*K - C[1] - C[2];

                     ...

            為了簡化用W[1] = K - C[1];W[i] = W[i-1] + K - C[i-1],則:

                     Z[2] = Z[1] + W[1];

                     Z[3] = Z[1] + W[2];

                     Z[i] = Z[1] + W[i-1];

            現在看看我們要求的目標函數是什麼;

            我們要求傳遞的硬幣最少,其實就是SUM(|Z[i]|)最小;

            利用上面推導的公式得到目標值關於Z[i]的函數關係;

                   f(Z[i])= | Z[1]-0 | +|Z[2] - W[2]| + ... + |Z[i] - W[i]| + ... + |Z[n] - W[n]|;

            可以看出中位數是最優解。

說明:注意用long long類型。

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>

using namespace std;

long long coins[1000001];
long long w[1000001];

int main()
{
	int n;
	while (~scanf("%d",&n)) {
		long long sum = 0LL;
		for (int i = 0; i < n; ++ i) {
			scanf("%lld",&coins[i]);
			sum = sum + coins[i];
		}
		
		long long ave = sum/n; 
		w[0] = 0LL;
		for (int i = 1; i < n; ++ i) {
			w[i] = w[i-1] + ave - coins[i];
		}
		
		sort(w, w+n);
		long long mid = w[n/2];
		long long ans = 0LL;
		for (int i = 0; i < n; ++ i) {
			ans = ans + abs(mid-w[i]);
		}
		
		printf("%lld\n",ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值