旅游问题,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