P3878 [TJOI2010]分金币【模拟退火】
洛谷P3878 -> Click Here
题意
有 \(n\) 枚硬币,第 \(i\) 枚硬币的价值为 \(v_i\) ,分为两堆,两堆硬币个数相差不能超过一,使两堆硬币价值只差最小
思路
玄学退火
在退火过程中随机改变数列中两个数字的位置,按照前半数硬币为一堆,后半数硬币为一堆的分配方法,逐渐降低温度求最优解
code
#include<iostream>
#include<cstdlib>
#include<ctime>
#include<cmath>
#define inf 2147483647
#define REP(i,a,b) for(register int i=(a);i<=(b);i++)
#define FOR(i,a,b) for(int i=(a);i<(b);i++)
using namespace std;
int n,ans=inf,a[1005];
int get(){
int sum1=0,sum2=0;
REP(i,1,(n+1)/2) sum1+=a[i];
REP(i,(n+1)/2+1,n) sum2+=a[i];
return abs(sum1-sum2);
}
void sa(){
double beginT=5000,endT=1e-10,changeT=0.996;
for(register double T=beginT;T>endT;T*=changeT){
int x=rand()%n+1,y=rand()%n+1;
swap(a[x],a[y]);
int sum=get();
if(sum<ans) ans=sum;
else if(exp((ans-sum)/T)<(double(rand())/RAND_MAX))
swap(a[x],a[y]);
}
}
int main(){
srand(rand());
int T;cin>>T;
while(T--){
ans=inf;
cin>>n;
REP(i,1,n) cin>>a[i];
int ctrl=10;
while(ctrl--) sa();
cout<<ans<<endl;
}
}

浙公网安备 33010602011771号