旅游问题的状态压缩求解

旅游问题,hdu3001。

问题描述

经过这么多天的编码,阿克默先生想好好休息一下。所以旅行是最好的选择!他决定访问 n 城市 (他坚持看到所有的城市!他不介意哪个城市是他的起点站,因为超人可以带他到任何城市在第一,但只有一次), 当然,这里有 m 道路,像往常一样收费。但阿克默先生很容易感到无聊,以至于他不想去一个城市访问两次以上!他是如此的吝啬,他想尽量减少总费用!你看,他很懒。所以他向你求助。

题目的问题可以概述为每个城市走路线,可以走两遍但不能多也不能少于一次,如果为暴力算法复杂度为(2n)!,算法超时不适合,因此需要用状态压缩的方法。

首先,对问题进行分析,有三种情况,0访问,1访问,2访问,所以为三种方式,用三进制来解决方法。利用tri[i][j]的方式,i为路径,j为去城市的状态。这种情况下,有3的10次方的可能性,所以将数值定为tri[60000][11]。此外定义状态dp[i][j],当前城市位i,状态位从i出发访问剩下所有城市并回到起点的总和费用最小值。

#include<stdc++.h>
const int INF = 0x3f3f3f3f;
using namespace std;
int n, m;
int bit[12] = { 0,1,3,9,27,81,243,729,2187,6561,19683,59049 };
int tri[60000][11];
int dp[11][600000];
int graph[11][11];
void make_trb() {
	for (int i = 0; i < 59050; ++i) {
		int t = i;
		for (int j = 1; j <= 10; ++j) { tri[i][j] = t % 3; t /= 3; }
	}
}

接下来进行逐个分析,定义起点bit[i],int未知数flag,如果flag都为1的话,说明所有的城市都已经遍历过一次及以上。选定终点为,判断终点位是否为0,并且如果没有经过所有的点的话继续进行。当此类运算结束后,通过对每个flag的情况进行判定,寻找到最小费用的情况。

nt comp_dp() {
	int ans = INF;
	memset(dp, INF, sizeof(dp));
	for (int i = 0; i <= n; i++)
		dp[i][bit[i]] = 0;
	for (int i = 0; i < bit[n + 1]; i++) {
		int flag = 1;
		for (int j = 1; j <= n; j++) {
			if (tri[i][j] == 0) {
				flag == 0;
				continue;
			}
			if (i == j) continue;
			for (int k = 1; k <= n; k++) {
				int l = i - bit[j];
				if (tri[i][k] == 0) continue;
				dp[j][i] = min(dp[j][i], dp[k][l] + graph[k][j]);
			}
		}
		if (flag)
			for (int j = 1; j <= n; j++)
				ans = min(ans, dp[j][i]);
	}
	return ans;
}

最后在main函数种调用make_yrb()函数。输入n,m数值,调用存图过的graph,在while循环种输入a,b,c对graph的情况位置定义。最后调用comp_dp()函数输出得到答案。

int main() {
	make_trb();
	while (cin >> n >> m) {
		memset(graph, INF, sizeof(graph));
		while (m--) {
			int a, b, c;
			cin >> a >> b >> c;
			if (c < graph[a][b])  graph[a][b] = graph[b][a] = c;
		}
		int ans = comp_dp();
		if (ans == INF) cout << "-1" << endl;
		else          cout << ans << endl;
	}
	return 0;
}

该题状态压缩过后的复杂度为O(3^n*n^2)。

数媒201wpy

猜你喜欢

转载自blog.csdn.net/zjsru_Beginner/article/details/119977385