CODEVS 1344 線型網絡

這道題一看數據範圍就知道很不可作吧,實際上這就是類TSP問題,是沒有多項式時間的解法的。spa

那怎麼搞,只能向\(O(n!\cdot n)\)的邪惡暴力全排列低頭了?祭出模擬退火code

講一下主要思路吧,咱們將狀態就定義爲一個排列,那麼咱們考慮每一次降溫的時候怎麼搞出新的解。string

這TM還用想麼,rand兩個項出來交換一下就OJBK了io

關於新的狀態我也沒想到什麼高級的計算方法,直接\(O(n)\)遍歷吧class

然而事實證實這樣的正確率已經十分優秀了並且真正的搞TSP的SA也是這麼轉移新狀態的遍歷

注意一下種子的選取,交上去80+的就洗把臉從新交幾回就能夠A了方法

CODEim

#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;
}
相關文章
相關標籤/搜索