1. 程式人生 > 其它 >P2210 Haywire 【模擬退火】

P2210 Haywire 【模擬退火】

洛谷 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;}