EXPLAIN 的欄位有哪些,具有什麼含義
阿新 • • 發佈:2022-04-06
題意:
求簡單圖(無向、無重邊、無自環)中簡單環(不重複經過點/邊)的數量
\(1\le n\le 19\)
思路:
能不能暴力嗯dp?\(dp(i,j)\) 表示起點為 \(i\),終點為 \(j\) 的方案數。這樣會把非簡單環算進去。
考慮狀壓,\(f(S,i)\) 表示路徑上的所有點組成點集 \(S\),起點是 \(S\) 中編號最小的點(記為 \(start\))(思想類似 lowbit),終點是 \(i\) 的方案數。
由 \(i\) 擴充套件到點 \(j\):
-
如果 \(j\) 比 \(start\) 還小,忽略之
-
對於 \(j\in S\),如果 \(j\) 就是 \(start\)
-
對於 \(j\notin S\),更新 \(f(S,i)\to f(S+'j',i)\)
更新的順序:從小到大列舉所有可能的 \(S\) 就好了。這樣迴圈到 \(S\) 之前,\(S\) 一定被所有能擴充套件到 \(S\) 的狀態擴充套件過
注意每個簡單環被算了兩次(順時針和逆時針);另外每個 \(x\to y\to x\) 的只有兩條邊的環也是非法的,這種有 \(m\) 個
所以輸出 \((ans-m)/2\)
const signed N = 19; //開到21會MLE int n, m; bool g[N][N]; ll f[1<<N][N], ans; int get(int S) { //非空集S的起點 for(int i = 0; ; i++) if(S>>i&1) return i; } bool in(int S, int x) { //u是否在集合S中 return (S>>x&1); } signed main() { iofast; cin >> n >> m; for(int i = 1; i <= m; i++) { int u, v; cin >> u >> v; u--, v--; //二進位制,-1比較方便 g[u][v] = g[v][u] = 1; } for(int i = 0; i < n; i++) f[1<<i][i] = 1; //初始化 for(int S = 1; S < (1<<n); S++) { int start = get(S); for(int i = 0; i < n; i++) { if(!f[S][i]) continue; //不存在的狀態 if(g[i][start]) ans += f[S][i]; for(int j = start+1; j < n; j++) if(g[i][j] && !in(S,j)) f[S|(1<<j)][j] += f[S][i]; } } cout << (ans-m)/2; }