给定n个点(0...n - 1)的带权有向图,出从0出发经过每点恰好一次再回到的0,求所经过的边的总权值的最小值
n<=15,d(i, j) < 1000
方程为:dp[S][v] = min(dp[S + u][u], map[v][u])
dp[S][v]指的是已经经过S集合中的点(包括v),从v出发回到0所需要的最小权值;
因为上述的dp方程中,第一个下标是集合不是整数,不好直接做,那怎么办呢,
1:把各个S所能取到的值编码成整数,最直观的我们想到每个点都只有取和不取两种状态,所以可以想到用二进制编码(下面代码就是这种方法)
2:直接用STL的set模拟集合
样例:
5 8
4 0 7
4 1 6
3 4 3
0 3 4
2 0 4
0 1 3
2 3 5
1 2 5
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<map>
#include<cstring>
#define maxn 16
#define inf 1000000
using namespace std;
int n, m;
int Map[maxn][maxn];
int dp[1 << maxn][maxn];
int Next[maxn];
void pre()
{
fill(Map[0], Map[0] + maxn * maxn, inf);
memset(dp, -1, sizeof(dp));
memset(Next, -1, sizeof(Next));
}
int solve(int s, int v)
{
if(dp[s][v] != -1)
return dp[s][v];
if(s == (1 << n) - 1 && v == 0)
return dp[s][v] = 0;
int Min = inf;
for(int i = 0; i < n; i++)
{
if(!(1 & s >>i) && Min > solve(s | 1 << i, i) + Map[v][i])
{
Min = solve(s | 1 << i, i) + Map[v][i];
Next[v] = i;
}
}
return dp[s][v] = Min;
}
int main()
{
cin >>n >> m;
pre();
for(int i = 0; i < m; i++)
{
int u, v, c;
cin >> u >> v >> c;
Map[u][v] = c;
}
cout << solve(0, 0) << endl;
//下面输出该回路
cout << 0;
for(int i = Next[0]; 1; i = Next[i])
{
printf("->%d", i);
if(!i)
break;
}
return 0;
}
本文介绍了一种使用动态规划解决旅行商问题(TSP)的方法,适用于小规模问题(n<=15),并给出了具体的C++实现代码。通过状态压缩将所有可能经过的节点集合作为状态的一部分,递归地寻找从任意一点出发回到起点的最小路径。
-状压dp&spm=1001.2101.3001.5002&articleId=78221381&d=1&t=3&u=f51dc3e32b214186a1f9cae3da218303)
1222

被折叠的 条评论
为什么被折叠?



