算法 分金币

圆桌旁坐着n个人,每人有一定数量的金币,金币总数总能被n整除。每个人可以给他左右相邻的人一些金币,最终使得每个人的金币数相等,你的任务是求出被转手的金币数量的最小值。

输入第一行为整数n(n<=1000000)以下n行每行为一个整数,按逆时针顺序给出每个人拥有的金币数。

对于每组数据输出被转手金币数量的最小值。

eg:

3

100

100

100

0

4

1

2

5

4

4

初看与蓝桥的海盗分金问题类似,单仔细的分析会发现完全不同,此题与海盗分金相比更麻烦复杂;

题目数据已知每人手中金币数,则易求出总金币数MZ=x1+x2+x3+...xn;

则最终局面每人手中的金币数M=MZ/n;

则对于初始局面x1,x2,x3,x4;设在整个过程中总发生第n人给第n-1人An枚金币(An可为负),而对于n=1时为第一人给第n个人金币数。

则对于此题结果为MIN{A1+A2+A3+A4};

则可得下式:

x1-A1+A2=M;=>A1=x1+A2-M;

x2-A2+A3=M;=>A2=x2+A3-M;

x3-A3+A4=M;=>A3=x3+A4-M;

x4-A4+A1=M;=>A4=x4+A1-M;

明显对于该式虽然有四个未知数四个等式,但并不能计算的出结果。即在四个方程中只有3个可用,即对于n个则前n-1个可用;

所以设A1已知;可推导出

A1

A2=A1+M-x1;

A3=A1+2M-x1-x2;

A4=A1+3M-x1-x2-x3;

则结果为MIN{A1+(A1+M-x1)+(A1+2M-x1-x2)+(A1+2M-x1-x2-x3)}

=>MIN{A1+(A1+M-x1)+(A1+2M-(x1+x2))+(A1+2M-(x1+x2+x3))}

=>MIN{A1+(A1-[x1-M])+(A1-[(x1+x2)-2M])+(A1-[(x1+x2+x3)-2M])}

在此处即可求出满足条件的A1(之后依据之前的等式可算出);

{

最后一步问题可等价为有n个点分布于一个数轴上从中选出一个点使其到其余各点的距离和最小

}

#include<stdio.h>
#define INF 99999999
void compare(int x,int y);
long long pe[1000001]={},sum[1000001]={};
int main()
{
	int n=1000000;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&pe[i]);
		sum[i]=sum[i-1]+pe[i];
	}
	int m=sum[n]/n;
	for(int i=2;i<=n;i++)
		pe[i]=sum[i-1]-(i-1)*m;
	pe[1]=0;
	compare(1,n);
	int num=pe[(1+n)/2];
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		int g=num-pe[i];
		ans+=g<0?-g:g;
	}
	printf("%d",ans);
	return 0;
 }
void compare(int x,int y)
{
	if(x>=y)	return ;
	int r,l,cur;
	l=cur=x;r=y;
	while(l<r)
	{
		for(;l<r;r--)
			if(pe[r]<pe[cur])
				{int t=pe[r];pe[r]=pe[cur];pe[cur]=t;cur=r;break;}
		for(;l<r;l++)
			if(pe[l]>pe[cur])
				{int t=pe[l];pe[l]=pe[cur];pe[cur]=t;cur=l;break;}
	}
	compare(x,cur-1);
	compare(cur+1,y);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值