1. 程式人生 > >poj 3311 Hie with the Pie 經過所有點(可重)的最短路徑 狀壓dp

poj 3311 Hie with the Pie 經過所有點(可重)的最短路徑 狀壓dp

n) 一個點 markdown 當前 stream 長度 strong inline 狀態

題目鏈接

題意

給定一個\(N\)個點的完全圖(有向圖),求從原點出發,經過所有點再回到原點的最短路徑長度(可重復經過中途點)。

思路

因為可多次經過同一個點,所以可用floyd先預處理出每兩個點之間的最短路徑。

接下來就是狀壓dp的部分。

將已經經過的點的狀態用\(state\)表示,

\(dp[state][k]\)表示當前到達點\(k\)後狀態為\(state\)時的最短路徑長度。

\[ans=min_{i=1}^{n}(dp[(1<<n)-1][i]+dis[i][0])\]

可用記憶化搜索

Code

#include <cstdio>
#include <iostream>
#include <cstring> #include <climits> #define F(i, a, b) for (int i = (a); i < (b); ++i) #define F2(i, a, b) for (int i = (a); i <= (b); ++i) #define dF(i, a, b) for (int i = (a); i > (b); --i) #define dF2(i, a, b) for (int i = (a); i >= (b); --i) #define maxn 12 #define maxs 1100
using namespace std; typedef long long LL; int n, a[maxn][maxn], dp[maxs][maxn]; bool vis[maxs][maxn]; void floyd() { F2(k, 0, n) { F2(i, 0, n) { F2(j, 0, n) { if (i==j||i==k||j==k) continue; a[i][j] = min(a[i][j],a[i][k]+a[k][j]); } } } } int
dfs(int state, int p) { if (state==(1<<(p-1))) return dp[state][p] = a[0][p]; if (vis[state][p]) return dp[state][p]; vis[state][p] = true; int ans = INT_MAX, sta = state&~(1<<(p-1)); F2(i, 1, n) { if (state&(1<<(i-1)) && i!=p) { ans = min(ans, dfs(sta, i)+a[i][p]); } } return dp[state][p] = ans; } void work() { memset(dp, 0, sizeof dp); memset(vis, 0, sizeof vis); F2(i, 0, n) { F2(j, 0, n) { scanf("%d", &a[i][j]); } } floyd(); int ans = INT_MAX; F2(i, 1, n) ans = min(ans, dfs((1<<n)-1, i)+a[i][0]); printf("%d\n", ans); } int main() { while (scanf("%d", &n) != EOF && n) work(); return 0; }

poj 3311 Hie with the Pie 經過所有點(可重)的最短路徑 狀壓dp