【思维题】uva11300Spreading the Wealth

来源:互联网 发布:最优化理论 课程 推荐 编辑:程序博客网 时间:2024/06/15 00:44

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 intusing 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;}

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

0 0
原创粉丝点击