1. 程式人生 > >hdu 3001 Travelling 經過所有點(最多兩次)的最短路徑 三進制狀壓dp

hdu 3001 Travelling 經過所有點(最多兩次)的最短路徑 三進制狀壓dp

所有 sin 狀態 三進制狀壓dp math 移位 sizeof bits 數據

題目鏈接

題意

給定一個\(N\)個點的無向圖,求從任意一個點出發,經過所有點的最短路徑長度(每個點至多可以經過兩次)。

思路

狀態表示、轉移及大體思路 與 poj 3311 Hie with the Pie 經過所有點(可重)的最短路徑 floyd + 狀壓dp 相同。

但,因為是每個點 至多可以經過兩次,所以應該用 三進制 來表示狀態。

因為三進制不能直接通過移位來表示,所以要 預處理 出每個數字\(state\)的三進制表示中每一位\(i\)上的值\(dig[state][i]\).

註意:該題數據中有 重邊

Code

#include <bits/stdc++.h>
#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 inf 0x3f3f3f3f #define maxs 60010 #define maxn 12 using namespace std; typedef long long LL; int power3[] = {1, 3, 9,27,81,243,729
,2187,6561,19683,59049}; int n, m, dp[maxs][maxn], dig[maxs][maxn], dis[maxn][maxn]; bool vis[maxs][maxn]; void init() { F(i, 0, power3[n]) { int state = i, cnt = 0; while (state) { dig[i][cnt++] = state%3; state /= 3; } } } int dfs(int state, int p) { if
(state == power3[p]) return 0; if (vis[state][p]) return dp[state][p]; vis[state][p] = true; int sta = state - power3[p], ans = inf; F(i, 0, n) { if (dis[i][p] && dig[state][i] && (i!=p || (i==p&&dig[state][i]==2))) ans = min(ans, dfs(sta, i)+dis[i][p]); } return dp[state][p] = ans; } inline bool check(int state) { F(i, 0, n) if (!dig[state][i]) return false; return true; } void work() { memset(dp, 0, sizeof dp); memset(vis, 0, sizeof vis); memset(dis, 0, sizeof dis); init(); F(i, 0, m) { int u, v, w; scanf("%d%d%d", &u, &v, &w); --u, --v; if (dis[u][v]==0 || w<dis[u][v]) dis[u][v] = dis[v][u] = w; } int lim = power3[n]-1, ans = inf; F2(i, lim>>1, lim) { if (!check(i)) continue; F(j, 0, n) { ans = min(ans, dfs(i, j)); } } printf("%d\n", ans==inf?-1:ans); } int main() { while (scanf("%d%d", &n,&m) != EOF) work(); return 0; }

hdu 3001 Travelling 經過所有點(最多兩次)的最短路徑 三進制狀壓dp