這道題一看數據範圍就知道很不可作吧,實際上這就是類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; }