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;}
posted @ 2021-08-06 16:43  莳曳  阅读(54)  评论(0)    收藏  举报