UVA 11300 Spreading the Wealth (数学推导)

本文探讨了如何通过数学抽象解决最小金币转移问题,详细解释了从动态规划误解到纯数学解法的过程,并提供了实现该算法的代码示例。

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

Problem

A Communist regime is trying to redistribute wealth in a village. They have have decided to sit everyone around a circular table. First, everyone has converted all of their properties to coins of equal value, such that the total number of coins is divisible by the number of people in the village. Finally, each person gives a number of coins to the person on his right and a number coins to the person on his left, such that in the end, everyone has the same number of coins. Given the number of coins of each person, compute the minimum number of coins that must be transferred using this method so that everyone has the same number of coins.

The Input

There is a number of inputs. Each input begins withn(n<1000001), the number of people in the village.nlines follow, giving the number of coins of each person in the village, in counterclockwise order around the table. The total number of coins will fit inside an unsigned 64 bit integer.

The Output

For each input, output the minimum number of coins that must be transferred on a single line.

Sample Input

3
100
100
100
4
1
2
5
4

Sample Output

0
4

【思路分析】

   乍一看像是DP问题,推了很久才发现是一个纯数学问题,关键是对问题的抽象。

   有n个人围成一圈,每个人分有若干枚金币,设分别为a1,a2,......,an,一共有m枚金币,并且 m % n == 0,要求使得每个人的金币数相等的最少转移金币数。每个人的金币来自其左右两人,但是可以等效为来自其中一个人(类似于运动学里面的等效运动,因为金币数是“守恒”的),并且每个人最终的金币数一定为 m / n,令A = m / n,则有下列等式成立:

   a1 + xn - x1 = A

   a2 + x1 - x2 = A

   a3 + x2 - x3 = A

   .............

  an + x(n - 1) - xn = A
  其中xi为i给i + 1的金币数,由上式可以根据x1推导得到x2,x3.....xn,即用x1来表达其他位置的转移值,即:

   x1 = x1

   x2 = x1 - (A - a2)

   x3 = x1 - (2 * A - a2 - a3)

   ...........

   令bi为x(i + 1)等式中右边的常数项,即num = |x1 - b0| + |x1 - b1| + |x1 - b2| + .......+|x1 - b(n - 1)|。推到这里就可以看出num的最小值取决于在数轴上x1据b0,b1,b2,.....,b(n- 1)的距离之和。这个问题在初中就涉及过,当x1是b0,b1,b2,........,b(n - 1)的中位数时,num取得最小值。


代码如下:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 1000005
long long a[maxn],b[maxn];
int main()
{
    int n;
    while(scanf("%d",&n) != EOF)
    {
        long long sum = 0;
        for(int i = 0;i < n;i++)
        {
            scanf("%lld",&a[i]);
            sum += a[i];
        }
        long long A = sum / n;
        b[0] = 0;
        for(int i = 1;i < n;i++)
        {
            b[i] = b[i - 1] + A - a[i];
        }
        sort(b,b + n);
        long long median = b[n / 2];//中位数
        long long ans = 0;
        for(int i = 0;i < n;i++)
        {
            ans += abs(median - b[i]);
        }
        printf("%lld\n",ans);
    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值