洛谷 P3878 [TJOI2010]分金幣 題解報告
阿新 • • 發佈:2021-11-28
題目連結
此題與 P2210 最大區別是如果產生的值不如原先值更優,需要把兩個金幣的位置再換回去、
點選檢視程式碼
#include<bits/stdc++.h> #define d 0.996//降溫幅度 #define lim 1e-10//停止溫度 #define INF 1e9+7 using namespace std; int n,t; int a[35]; int ans=INF; int del,now,nowx,nowy; int calc() { int sum1=0,sum2=0; for(int i=1;i<=n/2;i++) sum1+=a[i];//前一半 for(int i=n/2+1;i<=n;i++) sum2+=a[i];//後一半 return abs(sum1-sum2);//價值差 } void sa() { double T=2021;//初始溫度 while(T>lim) { nowx=rand()%n+1;//產生1~n內的兩個隨機種子 nowy=rand()%n+1; swap(a[nowx],a[nowy]);//交換兩個金幣的值 now=calc(); del=now-ans; if(del<0) ans=now;//now比ans小,說明此時now更優 else if(exp(-del/T)<(double)rand()/RAND_MAX){//否則一定概率接受此解 } else swap(a[nowx],a[nowy]);//如果產生的now不如ans更優,需要把兩個值再換回去,以此來穩定最優解 T*=d; } } } void work() { for(int i=1;i<=100;i++) sa();//多跑幾次 } int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } int main() { t=read(); while(t--) { n=read(); for(int i=1;i<=n;i++) a[i]=read();//輸入 ans=INF;//開始答案要賦極大值 work(); printf("%d\n",ans); } return 0; }