首页 > 其他分享 >状态压缩dp

状态压缩dp

时间:2023-02-12 15:22:47浏览次数:37  
标签:状态 终点 int 假设 压缩 距离 Hamilton 短距离 dp

最短Hamilton路径

给定一张 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

状态压缩DP分析:

1.本题思路

假设:一共有六个点,用0,1,2,3,4,5来表示,那么先假设终点就是5,在这里我们再假设还没有走到5这个点,且走到的终点是4,那么有以下六种情况:

first: 0–>1–>2–>3–>4 距离:21
second: 0–>1–>3–>2–>4 距离:23
third: 0–>2–>1–>3–>4 距离:17
fourth: 0–>2–>3–>1–>4 距离:20
fifth: 0–>3–>1–>2–>4 距离:15
sixth: 0–>3–>2–>1–>4 距离:18

如果此时你是一个商人你会走怎样的路径?显而易见,会走第五种情况对吧?因为每段路程的终点都是4,且每种方案的可供选择的点是04,而商人寻求的是走到5这个点的最短距离,而`4到5的走法只有一种`,所以我们选择第五种方案,可寻找到走到5这个点儿之前,且终点是4的方案的最短距离,此时05的最短距离为(15+4走到5的距离).(假设4–>5=8)

同理:假设还没有走到5这个点儿,且走到的终点是3,那么有一下六种情况:

first: 0–>1–>2–>4–>3 距离:27
second: 0–>1–>4–>2–>3 距离:22
third: 0–>2–>1–>4–>3 距离:19
fourth: 0–>2–>4–>1–>3 距离:24
fifth: 0–>4–>1–>2–>3 距离:26
sixth: 0–>4–>2–>1–>3 距离:17

此时我们可以果断的做出决定:走第六种方案!!!,而此时0~5的最短距离为(17+3走到5的距离)(假设3–>5=5)

在以上两大类情况之后我们可以得出当走到5时:
1.以4为终点的情况的最短距离是:15+8=23;
2.以3为终点的情况的最短距离是:17+5=22;
经过深思熟虑之后,商人决定走以3为终点的最短距离,此时更新最短距离为:22。

当然以此类推还会有以1为终点和以2为终点的情况,此时我们可以进行以上操作不断更新到5这个点的最短距离,最终可以得到走到5这个点儿的最短距离,然后再返回最初的假设,再依次假设1,2,3,4是终点,最后再不断更新,最终可以得出我们想要的答案

2.DP分析:

二进制来表示要走的所有情况的路径,这里用i来代替
例如走0,1,2,4这三个点,则表示为:10111;
走0,2,3这三个点:1101;

状态计算:如1中分析一致,0–>·····–>k–>j中k的所有情况

状态转移方程:f[i][j]=min(f[i][j],f[i-(1<<j)][k]+w[k][j])

代码

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N = 20, M = 1 << N;//M=2^N 

//i表示那些点都被用过,j表示目前走到哪个点 
//f[i][j]表示所有从0走到j,走过的所有点是i的所有路径 
int f[M][N], w[N][N]; //w表示的是带权无向图

int main() {
	int n;
	cin >> n;

	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			cin >> w[i][j];

	memset(f, 0x3f, sizeof(f)); //因为要求最小值,所以初始化为无穷大
	f[1][0] = 0; //因为零是起点,所以f[1][0]=0;

	for (int i = 0; i < (1 << n); i++) //i表示所有的情况
		for (int j = 0; j < n; j++) //j表示走到哪一个点
			if ((i >> j )& 1)//i>>j表示i的第j位为1,意思就是点j在状态i里 
				for (int k = 0; k < n; k++) //k表示走到j这个点之前,以k为终点的最短距离
					if ((i >> k )& 1)//如果k在状态i里 
						f[i][j] = min(f[i][j], f[i - (1 << j)][k] + w[k][j]); //更新最短距离

	cout << f[(1 << n) - 1][n - 1] << endl; //表示所有点都走过了,且终点是n-1的最短距离
	//位运算的优先级低于'+'-'所以有必要的情况下要打括号
	return 0;
}

标签:状态,终点,int,假设,压缩,距离,Hamilton,短距离,dp
From: https://www.cnblogs.com/wustRen/p/17113857.html

相关文章