ATC-arc078Fspa
題意概要:給定一個 \(n\) 點 \(m\) 邊簡單無向圖(無自環無重邊),邊有費用,現切去若干條邊,使得從 \(1\) 到 \(n\) 有且僅有一條簡單路徑,求最小化花費。code
\(n\le 15, n-1\le m\le \binom n2\)get
看到 \(n\leq 15\) 大概就能猜到複雜度是 \(O(3^n)\) 左右的,而後直接思考用斯坦納樹咋解,無果。io
開始思考最終局面的狀況,必定是有一條 \(1\) 到 \(n\) 的路徑,且不能存在其餘路徑鏈接這條路徑上的兩個點。換句話說,就是從每一個點出發且只走非路徑邊的話只能到達一個路徑上的點。class
考慮原問題要求最小化刪邊費用,對應上面的作法容易想到要轉換爲最大化留下來的邊權和test
把圖畫出來,大概是說路徑上每一個點下頭都掛上了若干個集合,而因爲要最大化比邊權和,因此每一個集合內部的全部邊均予以保留。集合
那麼就很容易獲得一個dp:設 \(f[i][S]\) 表示在路徑(最終鏈接 \(1\) 和 \(n\) 的路徑)上走到了 \(i\),而且集合 \(S\) 內的點已經掛在了前頭,那麼有兩種轉移:di
那麼只要預處理一個 \(g[S]\) 表示集合 \(S\) 內的邊權和,這一步能夠暴力求,複雜度 \(O(2^nn^2)\)思考
再進行轉移,轉移時須要枚舉子集(對於全局而言,枚舉補集的子集和枚舉子集複雜度至關),複雜度 \(O(n3^n)\)co
總複雜度 \(O(2^nn^2+n3^n)\)
#include <cstdio> #define bin(x) (1<<(x)) #define b(x) (1<<(x)>>1) inline void cmax(int&x, const int y) {x < y && (x = y);} const int N = 17, M = 40100; int f[N][M], g[M]; int mp[N][N]; int n, m; int main() { scanf("%d%d",&n,&m); int Ans = 0; for(int i=1,x,y,z;i<=m;++i) { scanf("%d%d%d",&x,&y,&z), Ans += z; mp[x][y] = mp[y][x] = z; } for(int s=0;s<bin(n);++s) { int&v = g[s]; for(int i=1;i<=n;++i) if(s & b(i)) for(int j=i+1;j<=n;++j) if(s & b(j)) v += mp[i][j]; } for(int i=1;i<=n;++i) for(int j=0;j<bin(n);++j) f[i][j] = -1; f[1][1] = 0; for(int S=1;S<bin(n);++S) for(int i=1;i<=n;++i) if((S & b(i)) and ~f[i][S]) { for(int j=1;j<=n;++j) if((~S & b(j)) and mp[i][j]) cmax(f[j][S|b(j)], f[i][S] + mp[i][j]); for(int iS=(bin(n)-1)^S, s = iS; s; s = (s-1) & iS) cmax(f[i][S|s], f[i][S] + g[s|b(i)]); } printf("%d\n", Ans - f[n][bin(n)-1]); return 0; }