1. 程式人生 > >位運算_CH0103_最短Hamilton路徑

位運算_CH0103_最短Hamilton路徑

思路分析:

    本題是典型的二進位制狀態壓縮DP問題(為方便敘述, 約定點i表示編號為i的點, 整數的最低位為第0位), 設F[i][j]表示從點0的點出發到點j, 且經過點的狀態為i的最短路徑長(路徑上所有邊的權值之和), 是如何表示經過點的狀態的? 如果i的第k(k >= 0)位為1那麼該路徑經過點k, 為0則不經過點k, 最終需求得F[(1 << n) - 1][n - 1] 欲求F[i][j], 可考察其對應路徑的倒數第2個頂點(如果存在)t, 初始化F[1][0]為0, 下面給出狀態轉移方程:

注意: i & ~(1 << j)表示將i的第j位賦0得到的值, weight[t][j]表示邊<t, j>(如果存在)的權值

F[]i][j] = min{ F[i & ~(1 << j)][t] + weight[t][j] | t \neq j, i 的第j位為1, i的第t位為1,  存在邊<i, j>} 

下面給出AC程式碼:

//CH0103_最短Hamilton路徑
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX = 20; 
int F[1 << MAX][MAX], weight[MAX][MAX];
int main(){
	int n;
	scanf("%d", &n);
	for(int i = 0; i < n; ++i)
		for(int j = 0; j < n; ++j) scanf("%d", &weight[i][j]);
	memset(F, 0x3f, sizeof(F)), F[1][0] = 0;//前者將F中所有元素初始化為正無窮大, 是必要的 
	for(int i = 2; i < (1 << n); ++i)
		for(int j = 0; j < n; ++j)
			if((i >> j) & 1)
				for(int t = 0; t < n; ++t)
					if(j != t && (i >> t) & 1)
						F[i][j] = min(F[i][j], F[i & ~(1 << j)][t] + weight[t][j]);
	cout << F[(1 << n) - 1][n - 1] << endl;
	return 0;
} 

對於上述演算法的時間複雜度, 顯然為O(2^{n}n^{2})