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;
}
辣鸡老年选手AFO在即