Solution -「Gym 102759C」Economic One-way Roads
阿新 • • 發佈:2021-08-15
\(\mathcal{Description}\)
Link.
給定一個含 \(n\) 個點 \(m\) 條邊的簡單無向圖,每條邊的兩種定向方法各有權值,求使得圖強連通且定向權值和最小的方法。
\(n\le 18\)。
\(\mathcal{Solution}\)
涉及到叫做“耳分解”的知識點。
有向圖 \(G=(V,E)\) 是否強連通有以下判別方法:
- 取任意 \(u\in V\),令點集 \(S=\{u\}\);
- 反覆取 \(x,y\in S\),以及連線 \(x,y\) 的一條有向路徑 \(P=\lang x,u_1,\cdots,u_k,y\rang\),滿足 \(u_i\not\in S,~i\in[1,k]\)
,並令 \(S\leftarrow S\cup\{u_1,\cdots,u_k\}\)。- 若 \(S=V\),則 \(G\) 強連通;否則即找不到增廣路 \(P\),\(G\) 非強連通。
其中 \(P\) 就是一個“耳”,這就是“耳分解”。
——當然“耳”貌似最初定義於無向圖。
知道了這個構造強連通圖的 trick 就極簡了,首先在雙向邊權中隨便選一個預支付代價,並令 \(f(S)\) 表示在 \(S\) 的匯出子圖內使 \(S\) 強連通的最小代價,\(g(S,x,y,0/1)\) 表示點集 \(S\) 中,當前正在構造的“耳”從 \(x\) 出發,希望回到 \(y\),不能/能 直接走 \(\lang x,y\rang\)
\(\mathcal{Code}\)
/*~Rainybunny~*/ #include <cstdio> #include <cstring> #define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i ) #define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i ) const int MAXN = 18, IINF = 0x3f3f3f3f; int n, adj[MAXN + 5][MAXN + 5], f[1 << MAXN], g[1 << MAXN][MAXN][MAXN][2]; inline void chkmin( int& a, const int b ) { b < a && ( a = b ); } inline int imin( const int a, const int b ) { return a < b ? a : b; } int main() { // freopen( "data.in", "r", stdin ); scanf( "%d", &n ); rep ( i, 0, n - 1 ) rep ( j, 0, n - 1 ) scanf( "%d", &adj[i][j] ); int ans = 0; rep ( i, 0, n - 1 ) rep ( j, i + 1, n - 1 ) { if ( ~adj[i][j] ) { int t = imin( adj[i][j], adj[j][i] ); ans += t, adj[i][j] -= t, adj[j][i] -= t; } } memset( f, 0x3f, sizeof f ), memset( g, 0x3f, sizeof g ), f[1] = 0; rep ( S, 1, ( 1 << n ) - 1 ) if ( S & 1 ) { rep ( u, 0, n - 1 ) if ( S >> u & 1 ) { rep ( v, 0, n - 1 ) if ( S >> v & 1 && ~adj[u][v] ) { chkmin( f[S], g[S][u][v][1] + adj[u][v] ); } } rep ( u, 0, n - 1 ) if ( S >> u & 1 ) { rep ( v, 0, n - 1 ) if ( S >> v & 1 ) { chkmin( g[S][u][v][0], f[S] ); } } rep ( u, 0, n - 1 ) if ( S >> u & 1 ) { rep ( v, 0, n - 1 ) if ( S >> v & 1 ) { int* cur = g[S][u][v]; if ( cur[0] != IINF ) { rep ( w, 0, n - 1 ) if ( ~adj[u][w] && !( S >> w & 1 ) ) { chkmin( g[S | 1 << w][w][v][u != v], cur[0] + adj[u][w] ); } } if ( cur[1] != IINF ) { rep ( w, 0, n - 1 ) if ( ~adj[u][w] && !( S >> w & 1 ) ) { chkmin( g[S | 1 << w][w][v][1], cur[1] + adj[u][w] ); } } } } } printf( "%d\n", f[( 1 << n ) - 1] == IINF ? -1 : ans + f[( 1 << n ) - 1] ); return 0; }