CODEVS 1344 线型网络

这道题一看数据范围就知道很不可做吧,实际上这就是类TSP问题,是没有多项式时间的解法的。

那怎么搞,只能向\(O(n!\cdot n)\)的邪恶暴力全排列低头了?祭出模拟退火

讲一下主要思路吧,我们将状态就定义为一个排列,那么我们考虑每一次降温的时候怎么搞出新的解。

这TM还用想么,rand两个项出来交换一下就OJBK了

关于新的状态我也没想到什么高级的计算方法,直接\(O(n)\)遍历吧

然而事实证明这样的正确率已经十分优秀了而且真正的搞TSP的SA也是这么转移新状态的

注意一下种子的选取,交上去80+的就洗把脸重新交几次就可以A了

CODE

#include<cstdio>
#include<cctype>
#include<ctime>
#include<cstdlib>
#include<cstring>
#include<cmath>
using namespace std;
typedef double DB;
const int N=25;
const DB EPS=1e-12,dlt=0.989;
struct data
{
	DB x,y;
}a[N];
int num[N],temp[N],n;
DB dis[N][N],ans=1e9;
inline DB calc(int p,int q)
{
	return sqrt((a[p].x-a[q].x)*(a[p].x-a[q].x)+(a[p].y-a[q].y)*(a[p].y-a[q].y));
}
inline void swap(int &x,int &y)
{
	int t=x; x=y; y=t;
}
inline DB work(int p,int q)
{
	memcpy(temp,num,sizeof(temp)); swap(temp[p],temp[q]);
	DB tot=0; for (register int i=2;i<=n;++i) tot+=dis[temp[i-1]][temp[i]];
	return tot;
}
inline void Simulate_Anneal(void)
{
	DB res=1e9,T=2500.0;
	for (;T>EPS;T*=dlt)
	{
		int x=rand()%n+1,y=rand()%n+1;
		while (x==y) y=rand()%n+1; DB now=work(x,y);
		if (now<ans) ans=now;
		if (now<res||(DB)exp(res-now)/T>(DB)rand()/RAND_MAX) res=now,memcpy(num,temp,sizeof(num));
	}
}
int main()
{
	register int i,j,t=500; scanf("%d",&n); srand(time(0));
	if (n<=1) return puts("0"),0;
	for (i=1;i<=n;++i) scanf("%lf%lf",&a[i].x,&a[i].y);
	for (i=1;i<=n;++i)
	for (j=1;j<=n;++j)
	if (i^j) dis[i][j]=calc(i,j);
	while (t--)
	{
		for (i=1;i<=n;++i) num[i]=i;
		Simulate_Anneal();
	}
	return printf("%.2lf",ans),0;
}
posted @ 2018-09-04 20:47  空気力学の詩  阅读(144)  评论(0编辑  收藏  举报