给定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;
}