[NOIp提高组2017]宝藏

解题思路

初识模拟退火,于是拿这道题练练手

我们指定没有连边的结点连了一条权值为inf的边,成为完全图,避免了不存在的生成树

这样枚举根,然后每一次枚举都跑若干遍模拟退火

初始状态就是一张菊花图,父亲都指向当前为根的结点

每次产生相近解只需要随机改变一个结点的父亲,但是这样并不能保证产生的关系任然是一棵树

考虑到最多只有12个结点,每次跑一遍并查集暴力判断,可以归结到常数复杂度

多试几个种子就过了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>

int n,m,x,y,v;
long long M[20][20];
const long long inf=0x3f3f3f3f3f3f;
int Fa[20];

namespace SA{
	const double start_T=10000;
	const double delta_T=0.993;
	const double low_T=1e-12;

	long long calc(int now,int fa,int dep){
		long long ret=dep*M[fa][now];
		for (int i=1;i<=n;i++){
			if (Fa[i]!=now||i==Fa[i]) continue;
			ret+=calc(i,now,dep+1);
		}
		return ret;
	}

	namespace MFS{
		int B[20];
		inline void init(){for (int i=1;i<=n;i++) B[i]=i;}
		inline int find(int k){return (k==B[k])?(k):(B[k]=find(B[k]));}
		void merge(int a,int b){
			int fa=find(a),fb=find(b);
			if (fa==fb) return;
			B[fa]=fb;
		}
		inline bool same(int a,int b){return find(a)==find(b);}
	}

	bool allowed(){
		MFS::init();
		for (int i=1;i<=n;i++){
			if (i!=Fa[i]&&MFS::same(i,Fa[i])) return false;
			MFS::merge(i,Fa[i]);
		}
		return true;
	}

	long long ans;

	inline double rand_double(){return rand()/(double)RAND_MAX;}

	long long SA_main(int root){
		ans=calc(root,0,0);
		double T=start_T;
		while (T>low_T){
			int change,to;
			do{
				change=rand()%n+1,to=rand()%n+1;
			}while (change==to||change==root);
			int ec=Fa[change];
			Fa[change]=to;
			if (!allowed()){
				Fa[change]=ec;
				continue;
			}
			long long nxt=calc(root,0,0);
			if (nxt<ans||exp((ans-nxt)/T)>rand_double()) ans=nxt;
			else Fa[change]=ec;
			T*=delta_T;
		}
		return ans;
	}
}

long long Ans=inf;

int main(){
    srand(/**/);//种子被和谐了
	scanf("%d%d",&n,&m);
	if (n==1){printf("%d\n",0);return 0;}
	memset(M,0x3f,sizeof(M));
	for (int i=1;i<=m;i++){
		scanf("%d%d%d",&x,&y,&v);
		M[x][y]=M[y][x]=std::min(M[x][y],(long long)v);
	}
	for (int i=0;i<=n;i++)
		for (int j=0;j<=n;j++)
			M[i][j]=std::min(M[i][j],inf);
	for (int i=1;i<=n;i++){
		for (int j=1;j<=n;j++) Fa[j]=i;//初始状态菊花图
		Ans=std::min(Ans,SA::SA_main(i));
		Ans=std::min(Ans,SA::SA_main(i));
		Ans=std::min(Ans,SA::SA_main(i));
		Ans=std::min(Ans,SA::SA_main(i));
		Ans=std::min(Ans,SA::SA_main(i));
		Ans=std::min(Ans,SA::SA_main(i));
		Ans=std::min(Ans,SA::SA_main(i));
		Ans=std::min(Ans,SA::SA_main(i));
		Ans=std::min(Ans,SA::SA_main(i));
		Ans=std::min(Ans,SA::SA_main(i));//这里多做几次
	}
	printf("%lld\n",Ans);
}
posted @ 2018-09-26 11:46  ytxytx  阅读(207)  评论(0编辑  收藏  举报