【思维题】uva11300Spreading the Wealth

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2275
题目描述:环形排列的n (n<=106) 个人,每人有一定量的金币。每个人可以给左右相邻的两个人金币,最终使得每个人都有相同量的金币。求被转手的最小金币数。
样例输入:
3
100
100
100
4
1
2
5
4
样例输出:
0
4

方法一:
进行数学分析:
设最终每个人有m个金币。第i个人给左边的人(第i-1个人) xi 个金币。当然 xi 可以为负,这样表示第i-1个人给第i个人金币。
那么得到n-1个方程:
A1x1+x2=m ——> x2=mA1+x1=x1C1 (令 Ci=A1+A2+ +Aiim
A2x2+x3=m ——> x3=mA2+x2=x1C2
……
An1xn1+xn=m ——> xn=mAn1+xn1=x1Cn1
理论上来说应该还有一个方程式: Anxn+x1=m 然而这是一个多余的方程式,因为总人数一定。
现在要求的就是 x1+|x1C1|+|x1C2|+ +|x1Cn1| 的最小值。当 x1 为中位数时,该值最小。(可以去百度一下零点分段法)

求中位数的算法可以用sort计算得到,也可以用快速选择算法(建议去看一看《算法导论》,上面讲的十分清楚)。

蒟蒻不才,想了半天还是看了题解才做出来的……

#include <iostream>
#include <cstdio>
#include <algorithm>
#define abs(a) ((a)>0?(a):(-(a)))
#define MAXN 1000005
#define LL long long int
using namespace std;

int n ;
LL money[MAXN] ,num[MAXN] ,ans ,result ,sum ;

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

方法二:
xi i 个人给左边的人(第i1个人) xi 个金币。当然 xi 可以为负,这样表示第 i1 个人给第 i 个人金币。
对其中一组解,每一个xi都减去 b 则得到另一组解。现在要求它们绝对值之和最小,那么令b为中位数即可。
实现:
1.找到任意一组可行解。
2.找到中位数 b
3.计算|xib|之和

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值