分金币----拿模拟退火来做是不是有点过分了

题目描述

圆桌上坐着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;
}
 

猜你喜欢

转载自blog.csdn.net/Du_Mingm/article/details/84103126