hdu 3001 Travelling 經過所有點(最多兩次)的最短路徑 三進制狀壓dp
阿新 • • 發佈:2018-02-15
所有 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