题目描述
圆桌上坐着n个人,每人有一定数量的金币,金币总数能被n整除。每个人可以给他左右相邻的人一些金币,最终使得每个人的金币数目相等。你的任务是求出被转手的金币数量的最小值。
输入输出格式
输入格式:
第一行为整数n(n>=3),以下n行每行一个正整数,按逆时针顺序给出每个人拥有的金币数。
输出格式:
输出被转手金币数量的最小值。
输入输出样例
输入样例#1: 复制
4 1 2 5 4
输出样例#1: 复制
4 样例解释 设四个人编号为1,2,3,4。第3个人给第2个人2个金币(变成1,4,3,4),第2个人和第4个人分别给第1个人1个金币。
说明
N<=<=100000,总金币数<=10^9
因为太菜,所以并没有找出来中位数,拿模拟退火写的,中石油,ZCMU,洛谷(糖果传递那个题的数据太强的,氵不过去,只能用中位数)都过了
ZCMU上的是多组输入,所以这里是最后在ZCMU上提交的代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const double Esp = 1e-2,inf = 1e18,delta = 93e-2;
///Esp-->精度,initT-->初始步长,inf-->最大值,delta-->步长每次缩短的系数
const int k = 3,maxn=1e6+7;
///每次随机跑动几次
ll a[maxn],n,sum,b[maxn];
double Rand(){///随机数输出一个概率范围是[-1,1]
return rand()&1 ? 1.0*rand()/RAND_MAX : -1.0*rand()/RAND_MAX;
}
ll Fun(ll x){
double ans = fabs(x);
for(int j=2;j<=n;j++)
x += a[j] - sum,ans += fabs(x);
return ans;
/*double ans = 0;
for(int j=0;j<n;j++)
ans += abs(x-b[j]);
return ans;*/
}
ll Sovle(){///模拟退火
ll t = 1e9,ans = inf;///t-->初始温度(步长),ans-->答案
ll x = Rand()*1e9;///生成原始解,[0,100]
while(t>0){///步长咯
ll tfx = Fun(x);///函数值
for(int i=0;i<k;i++){///随机瞎跑,取最优解
ll tx = x + Rand()*t;///跑动范围(delta x) [-t,t],即最大步长内
if( tx + 1e9 >= 0 && tx - 1e9 <= 0){///在[0,100]范围内
ll ttfx = Fun(tx);///这次跑出去得到的函数值
if(ttfx < tfx){///如果比较优秀
tfx = ttfx;
x = tx;
}
}
ans = min(ans,tfx);///更新答案
}
t *= delta;///步长缩短
}
return ans;
}
int main(){
while(~scanf("%lld",&n)){
sum = 0;
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),sum += a[i];
sum /= n;
printf("%lld\n",Sovle());
}
return 0;
}