P3959 寶藏(狀壓 dp)
阿新 • • 發佈:2021-11-11
目錄
Description
一任意一個節點為根,每連線一個新的節點需要花費 \(dep*dis\), \(dis\) 為相鄰兩節點的距離,求最小花費,根的深度為 \(0\)
State
\(1<=n<=12\)
\(1<=m<=3000\)
\(1<=5*10^5\)
Input
4 5 1 2 1 1 3 3 1 4 1 2 3 4 3 4 1
Output
4
Solution
如果可以想出 \(dp\) 方程還是很好解決的,\(dp[i][j]\) 表示深度為 \(i\) 時,已經擴充套件的狀態為 \(j\) 的最小花費
那麼如果從 \(j\) 所構成的樹中擴充套件另一些節點,那麼最小花費需要加上 \(i*|S|\),其中 \(S\) 為擴充套件這些節點所需要的總距離
擴充套件結點的部分相當於列舉子集,時間複雜度為 \(O(3^n)\)
總時間複雜度為 \(O(n*3^n)\)
Code
const int S = (1 << 12) + 5; const int N = 1e5 + 5; int n, m, k, _; int a[N]; int dp[15][S]; int road[15][15]; int dis[S][S]; int cal(int now, int nxt) { int ans = 0; for(int i = 1; (1 << i - 1) <= nxt; i ++){ if((nxt & (1 << i - 1))){ int minn = 2e9; for(int j = 1; (1 << j - 1) <= now; j ++){ if((now & (1 << j - 1))){ minn = min(minn, road[i][j]); } } if(minn == 2e9) return minn; ans += minn; } } return ans; } signed main() { // IOS; while(~ sdd(n, m)){ rep(i, 1, n) rep(j, 1, n) road[i][j] = 2e9; rep(i, 1, m){ int x = read(), y = read(), w = read(); w = min(w, road[x][y]); road[x][y] = w; road[y][x] = w; } int all = 1 << n; rep(i, 0, n) rep(j, 0, all) dp[i][j] = 2e9; rep(i, 0, all) rep(j, 0, all) dis[i][j] = 2e9; for(int i = 1; i < all; i <<= 1){ dp[0][i] = 0; } for(int i = 0; i < all; i ++){ for(int j = i; j; j = (j - 1) & i){ int k = j ^ i; //j -> k,由 j 擴展出 k dis[j][k] = cal(j, k); } } for(int i = 1; i <= n; i ++){ for(int s = 0; s < all; s ++){ for(int j = s; j; j = (j - 1) & s){ int k = s ^ j; if(dis[j][k] == 2e9) continue; dp[i][s] = min(dp[i][s], dp[i - 1][j] + dis[j][k] * i); } } } int minn = inf; for(int i = 1; i <= n; i ++){ minn = min(minn, dp[i][all - 1]); } pd(minn); } // PAUSE; return 0; }