uva11300 - Spreading the Wealth

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.
Input
There is a number of inputs. Each input begins with n (n < 1000001), the number of people in the
village. n lines 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.
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
思路:
1.首先可以求到每个人需要的苹果M
2.然后每个人可以送苹果给别人或者向别人拿苹果,那么假设a1,他给4号x1个,2号给他x2个,那么a还剩下A1-x1+x2.这个数要等于M。其实x1和x2的正负就表示他是拿还是给予。
3.之后我们就有了递推公式
A1-x1+x2=M x2=x1+M-A1=x1-C1 (我们设Ci=Σ[1~i]{Ai-M})
A2-x2+x3=M x3=x1+M-A1+M-A2=x1-C2
A3-x3+x4=M x4=x1+M-A1+M-A2+M-A3=x1-C3
xi=x1-C[i-1]
4.目标求min(x1+x2+…xn)
那么就有x1+|x1−C1|+|x1−C2|+x1+|x1−C1|+|x1−C2|+…+|x1−Cn−1|+|x1−Cn−1|的最小值
这个最小值就是在中位数取得怎么证呢?我要借用白书了。
嘤嘤嘤♪(*)
5.证明:
给定数轴上n个点,在数轴上所有点之中,中位数离所有定点的距离之和最小
凡是能转化成这个模型的题目都可以用中位数求解

让我们把题目具象化一下:

任意找一个点,如图中的红点,ta的左边有4个点,右边有2个点,
把ta往左移动一点,但是不碰到定点
假设我们移动了d的距离,总的来说距离之和就减少了2d
换句话说,如果红点两边的定点数目不同时,该点就不是最优点,那什么时候该点的两边有相等数目的点呢
当然是在中点啊

在这里插入图片描述
输入点有奇数个,选中点就应该与中间的那个点重合(中位数)
输入点有偶数个,选中点就可以位于最中间两个点之间的任意位置

最后贡献上代码:(这里有一个优化,我们可以用调用stl中的nth_element(start, start+n, end) 方法可以使第n大元素处于第n位置(从0开始,其位置是下标为 n的元素),并且比这个元素小的元素都排在这个元素之前,比这个元素大的元素都排在这个元素之后,但不能保证他们是有序的。这个是logn级别的。但是我没打,你们可以试试)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long

using namespace std;

const int N=1000010;
int n;
ll C[N],a[N];

int main()
{
    while (scanf("%d",&n)!=EOF)
    {
        ll sum=0;
        for (int i=1;i<=n;i++) 
            scanf("%lld",&a[i]),sum+=a[i];
        sum/=n;                             //平均数         
        C[0]=0;
        for (int i=1;i<n;i++)
            C[i]=C[i-1]+a[i]-sum;           //xn=x1-C[n-1]
        sort(C,C+n);                        //一共n个元素 
        ll x1=C[n/2];
        ll ans=0;
        for (int i=0;i<n;i++) ans+=abs(x1-C[i]);
        printf("%lld\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值