二進制狀態壓縮DP

描述
給定一張 n 個點的帶權無向圖,點從 0~n-1 標號,求起點 0 到終點 n-1 的最短Hamilton路徑。 Hamilton路徑的定義是從 0 到 n-1 不重不漏地經過每個點恰好一次。
輸入格式
第一行輸入整數n。

接下來n行每行n個整數,其中第i行第j個整數表示點i到j的距離(記爲a[i,j])。

對於任意的x,y,z,數據保證 a[x,x]=0,a[x,y]=a[y,x] 並且 a[x,y]+a[y,z]>=a[x,z]。

輸出格式
輸出一個整數,表示最短Hamilton路徑的長度。

數據範圍
1≤n≤20
0≤a[i,j]≤107
輸入樣例:
5
0 2 4 5 1
2 0 6 5 3
4 6 0 8 3
5 5 8 0 5
1 3 3 5 0
輸出樣例:
18



一看到這道題目的時後我們一眼就覺得這是一道搜索題,但是我們仔細分析一下時間複雜度:
頭尾都是確定的,因此我們要對中間的(n - 2)個數進行全排列,大概就是(n - 2)!次方次排列,於是我們自然而然的就想到了用DP來絕決這個問題,但是用DP我們又該怎麼樣來表示這之間的狀態呢。

每一個位置我們用0,1,來表示。0代表沒走過,1代表已近走過,
由此我們就有了狀態轉移方程。
DP[A][j] = min(DP[A][j], DP[B][k] + value[k][j])
其中AB,分別時兩個集合。B集合加上點 j 等於A集合。
每個狀態用其第幾位的二進制是0或者1來表示
我們有了下面的代碼

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = (1 << 20) + 10;
int dp[maxn][22], value[22][22], n;
int main() {
	cin >> n;
	for(int i = 0; i < n; i++)
		for(int j = 0; j < n; j++)
			cin >> value[i][j];
	memset(dp, 0x3f, sizeof dp);
	dp[1][0] = 0;
	for(int i = 0; i < 1 << n; i++)
		for(int j = 0; j < n; j++)
			if(i >> j & 1)
				for(int k = 0; k < n; k++)
					if(i - (1 << j) >> k & 1)
						dp[i][j] = min(dp[i][j], dp[i - (1 << j)][k] + value[k][j]);
	cout << dp[(1 << n) - 1][n - 1] << endl;
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章