Android連線阿里雲(二)
阿新 • • 發佈:2022-05-19
題目連結:https://www.acwing.com/problem/content/93/
題目描述
給定一張 n 個點的帶權無向圖,點從 0∼n−1 標號,求起點 0 到終點 n−1 的最短 Hamilton 路徑。
Hamilton 路徑的定義是從 0 到 n−1 不重不漏地經過每個點恰好一次。
輸入描述
第一行輸入整數 n。
接下來 n 行每行 n 個整數,其中第 i 行第 j 個整數表示點 i 到 j 的距離(記為 a[i,j])。
對於任意的 x,y,z,資料保證 a[x,x]=0,a[x,y]=a[y,x] 並且 a[x,y]+a[y,z]≥a[x,z]。
輸出描述
輸出一個整數,表示最短 Hamilton 路徑的長度。
1≤n≤20
0≤a[i,j]≤10^7
示例
輸入
5
0 2 4 5 1
2 0 6 5 3
4 6 0 8 3
5 5 8 0 5
1 3 3 5 0
輸出
18
分析
約定
記從i到j的距離為s[i][j]
狀態表示
f(i,j)表示滿足如下條件行走方案的最小路徑長度:
- i為狀態量,表示行走時經過的點
- 從0點出發到達j
將i看作一個二進位制數,第k位不為0表示途徑k點,為0表示不經過k點。
狀態劃分與計算
對於f(i,j),我們還是按照以往的思路,試著將達成f(i,j)的最後一步操作進行劃分。
對應本題中,我們要關注的就是整個路徑中的倒數第二個點,通過對倒數第二個點是誰進行分類。
狀態轉移方程如下:
f(i,j)=min(f(i-(1<<j),k)+s[k][j])
還有一些處理詳見程式碼。
邊界處理
f(1,0)=0,其餘都是正無窮。
AC程式碼
#include <iostream> #include <algorithm> #include <vector> #include <cstring> using namespace std; const int N = 20, M = 1 << N; int n; int f[M][N]; int s[N][N]; int main() { cin >> n; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) cin >> s[i][j]; memset(f, 0x3f, sizeof(f)); f[1][0] = 0; for (int i = 0; i < 1 << n; i++) for (int j = 1; j < n; j++) if (i >> j & 1) //目的地是j 那i中j對應的那一位得是1 否則沒有必要繼續討論 for (int k = 0; k < n; k++) if ((i - (1 << j)) >> k & 1) //倒數第二個點對應的位也得是1 f[i][j] = min(f[i][j], f[i - (1 << j)][k] + s[k][j]); //最終解果是經過所有點到達n-1 cout << f[(1 << n) - 1][n - 1] << endl; return 0; }