P2210 Haywire 【模擬退火】
阿新 • • 發佈:2021-08-06
洛谷 P2210 -> Click Here
題意
有 \(N\) 頭 (\(4\leq N\leq 12\)) 奶牛,每頭奶牛有且只有 \(3\) 個朋友,如果奶牛 \(A\) 是奶牛 \(B\) 的朋友,那麼奶牛 \(B\) 也是奶牛 \(A\) 的朋友,將這些奶牛排列在整數軸上,兩隻奶牛的距離為兩隻奶牛之間的間隔的整數的個數,任意排列奶牛,求所有奶牛的朋友間的距離最小距離
思路
與P3837 分硬幣類似,都是對整數列進行隨機交換兩個數字,逐漸降溫,求最小值,詳情左轉 -> P3878 分金幣 模擬退火題解
本題對奶牛的順序進行隨機化,交換兩個奶牛的位置,檢視是否更優或在一定接受的概率內逐漸降溫,最後找到最小值
code
#include<iostream> #include<cstdlib> #include<ctime> #include<vector> #include<cmath> #include<algorithm> #define REP(i,a,b) for(int i=(a);i<=(b);i++) #define FOR(i,a,b) for(int i=(a);i<(b);i++) using namespace std; typedef long long ll; ll n; // int plac[15]; int fir[22][22]; int ans=99999999; vector<int> v[22]; vector<int> plac(22),ansv; int get(){ int res=0; REP(i,1,n) FOR(j,0,3) res+=abs(plac[v[i][j]]-plac[i]); return res; } void SA(){ double T=10000,down=0.996,eT=1e-15; while(T>eT){ int x=rand()%n+1,y=rand()%n+1; swap(plac[x],plac[y]); int nowans=get(); int del=nowans-ans; if(del<0) ans=nowans; else{ if(exp(- del/T)*RAND_MAX>rand()) swap(plac[x],plac[y]); } T*=down; } } void solve(){ srand(time(0)); cin>>n; REP(i,1,n){ plac[i]=i; int a,b,c; cin>>a>>b>>c; v[i].push_back(a); v[i].push_back(b); v[i].push_back(c); } int cnt=250; while(cnt--) SA(); cout<<ans/2<<endl; } int main(){solve();return 0;}