题意:
要求从1开始起,经过所有的点,最终回到1,所要的最小值。
解析:
这是一道比较经典的旅行商问题,以前做过类似的。
先用floyd求出所有点之间的最短路。
然后用状态压缩,16位每位上为1表示已走过,0表示没有走过,直接用位运算,表示所有的状态。
dp[st][j] 状态为 st ,最后一次走的是 j 点的最小值。
则状态转移方程为:
dp[st|(1<<(j−1))][j]=min(dp[st|(1<<(j−1))][j],dp[st][i]+d[i][j])
有 216∗n 个状态,n种转移,所以复杂度为 O(216∗n∗n)
my code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20;
const int INF = 0x3f3f3f3f;
int d[N][N];
int dp[1<<N][N];
int n, m;
void floyd() {
for(int i = 1; i <= n; i++) d[i][i] = 0;
for(int k = 1; k <= n; k++) {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}
}
int tsp() {
memset(dp,INF,sizeof(dp));
dp[1][1] = 0;
int end = (1<<n) - 1;
for (int st=1; st <=end; st++)
for(int i=1; i <= n; i++)
for(int j=1; j <= n; j++) {
if (i == j) continue;
if ((1 << (i-1)) & st == 0 || (1 << (j-1) & st == 1)) continue;
if (dp[st][i] == INF) continue;
dp[st|(1<<(j-1))][j]=min(dp[st|(1<<(j-1))][j],dp[st][i]+d[i][j]);
}
int ans = INF;
for (int i = 1; i <= n; i++)
ans=min(ans, dp[end][i]+d[i][1]);
return ans;
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &m);
memset(d, INF, sizeof(d));
int u, v, val;
for(int i = 0; i < m; i++) {
scanf("%d%d%d", &u, &v, &val);
d[u][v] = min(d[u][v], val);
d[v][u] = min(d[v][u], val);
}
floyd();
printf("%d\n", tsp());
}
return 0;
}