原題連接c++
N個點,從1號點到N號點求最短路徑,且每一個點都要遍歷到。如今要你求出最優方案。數組
這道題看到後,首先的想法莫過於搜索、暴力了。這顯然不太可能。而進一步思考,使用Floyed和Dijkstra也不太好用,由於題目描述說:"每一個點都要遍歷到",天然又否決了這個方法。那麼怎麼辦呢?spa
什麼是狀態壓縮?code
因爲全部點在DP階段中的狀態只有走過( true )和沒走過( false ),那麼用0、1的二進制表示每一個階段便可。blog
好比如今有5個點,如今只通過了二、三、5號點。那麼二進制就能夠這樣表示:get
01101
再轉換成十進制便可it
如何記錄階段?搜索
因此咱們的DP數組須要二維,一維記錄當前二進制狀態用十進制表示的數,另外一維記錄當前階段點,也就是當前點是否通過。遍歷
dp[i][j] 表示i狀態下走到j號點最優方案
狀態轉移方程是什麼?二進制
咱們只要再枚舉一個點,即枚舉上一個點,合法就取min值便可。
#include<bits/stdc++.h> using namespace std; const int MAXN = 20 + 5; int n,dp[(1 << 17)][MAXN]; int dis[MAXN][MAXN]; inline int read(){//快速讀入 int f = 1, x = 0; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); } return f * x; } int Hamilton(){ memset(dp,0x3f,sizeof(dp)); dp[1][1] = 0; for(int i = 1;i <= (1 << (n + 1)) - 1; i++){ for(int j = 1;j <= n; j++){ if(!((i >> j) & 1))continue; for(int k = 1;k <= n; k++){ if(!((i >> k) & 1))continue; dp[i][j] = min(dp[i][j],dp[i ^ (1 << j)][k] + dis[k][j]); } } } return dp[(1 << (n + 1)) - 1][n]; } int main(){ n = read(); for(int i = 1;i <= n; i++) for(int j = 1;j <= n; j++){ dis[i][j] = read(); } //dp[i][j]:i表示全部點壓縮後的狀態,j表示當前在的點 //則dp[(1 << (n + 1)) - 1][n]就是最後答案 cout<<Hamilton(); return 0; }