這道題正解是狀壓DP,不過我不會因此寫一下隨機化算法來騙騙分。git
據說當時考場上就有不少寫prim而後掛掉的神仙,其實這道題是能夠prim過的算法
prim是一種基於貪心的算法,在本題中因爲盲目的選擇當前最優解可能會使得後面的決策不優,因而咱們請出基於隨機化的prim我口胡的spa
每一次選擇邊的時候,有機率的跳過一些對於當前來講最優的邊,這樣爲後面可能跑出更優的解作出鋪墊。code
這樣一次可能獲得的解還不如直接貪心獲得的解優,可是我複雜度這麼小,跑個幾千次又怎樣?string
而後選好隨機數種子,滿懷信仰地祈禱就能夠AC了it
雖然這不是嚴格意義上的模擬退火,可是隨機化的思想仍是比較神仙的io
CODEclass
#include<cstdio> #include<cctype> #include<cstdlib> #include<cstring> #include<queue> #include<ctime> using namespace std; const int N=15,M=1005; int edge[N][N],n,m,x,y,z,dep[N],ans,INF; bool vis[N]; struct data { int fr,to; bool operator <(const data a) const { return dep[a.fr]*edge[a.fr][a.to]<dep[fr]*edge[fr][to]; } }stack[M]; priority_queue <data> small; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline int Simulate_Anneal(int st) { memset(dep,0,sizeof(dep)); memset(vis,0,sizeof(vis)); register int i; int tot=0,top=0; memset(stack,0,sizeof(stack)); while (!small.empty()) small.pop(); dep[st]=vis[st]=1; for (i=1;i<=n;++i) if (edge[st][i]<INF) small.push((data){st,i}); for (i=1;i<n;++i) { data e=small.top(); small.pop(); while (!small.empty()&&(vis[e.to]||!(rand()%n))) { if (!vis[e.to]) stack[++top]=e; e=small.top(); small.pop(); } vis[e.to]=1; dep[e.to]=dep[e.fr]+1; tot+=dep[e.fr]*edge[e.fr][e.to]; while (top) small.push(stack[top--]); for (register int j=1;j<=n;++j) if (!vis[j]&&edge[e.to][j]<INF) small.push((data){e.to,j}); } return tot; } inline void miner(int &x,int y) { x=y<x?y:x; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); register int i,t=200; read(n); read(m); srand(20030909); memset(edge,63,sizeof(edge)); ans=INF=edge[0][0]; for (i=1;i<=m;++i) { read(x); read(y); read(z); miner(edge[x][y],z); miner(edge[y][x],z); } while (t--) for (i=1;i<=n;++i) miner(ans,Simulate_Anneal(i)); return printf("%d",ans),0; }