TSP(旅行商問題)-狀壓dp
阿新 • • 發佈:2019-01-05
給定n個點(0...n - 1)的帶權有向圖,出從0出發經過每點恰好一次再回到的0,求所經過的邊的總權值的最小值
n<=15,d(i, j) < 1000
方程為:dp[S][v] = min(dp[S + u][u], map[v][u])
dp[S][v]指的是已經經過S集合中的點(包括v),從v出發回到0所需要的最小權值;
因為上述的dp方程中,第一個下標是集合不是整數,不好直接做,那怎麼辦呢,
1:把各個S所能取到的值編碼成整數,最直觀的我們想到每個點都只有取和不取兩種狀態,所以可以想到用二進位制編碼(下面程式碼就是這種方法)
2:直接用STL的set模擬集合
樣例:
5 8
4 0 7
4 1 6
3 4 3
0 3 4
2 0 4
0 1 3
2 3 5
1 2 5
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<map> #include<cstring> #define maxn 16 #define inf 1000000 using namespace std; int n, m; int Map[maxn][maxn]; int dp[1 << maxn][maxn]; int Next[maxn]; void pre() { fill(Map[0], Map[0] + maxn * maxn, inf); memset(dp, -1, sizeof(dp)); memset(Next, -1, sizeof(Next)); } int solve(int s, int v) { if(dp[s][v] != -1) return dp[s][v]; if(s == (1 << n) - 1 && v == 0) return dp[s][v] = 0; int Min = inf; for(int i = 0; i < n; i++) { if(!(1 & s >>i) && Min > solve(s | 1 << i, i) + Map[v][i]) { Min = solve(s | 1 << i, i) + Map[v][i]; Next[v] = i; } } return dp[s][v] = Min; } int main() { cin >>n >> m; pre(); for(int i = 0; i < m; i++) { int u, v, c; cin >> u >> v >> c; Map[u][v] = c; } cout << solve(0, 0) << endl; //下面輸出該回路 cout << 0; for(int i = Next[0]; 1; i = Next[i]) { printf("->%d", i); if(!i) break; } return 0; }