hdu 5418 Victor and World
经过多年的努力,维克多终于拿到了驾驶执照。为了庆祝一下,他打算给自己买一架飞机,飞遍全世界。地球上有n个国家,编号从1到n。它们由m个无向航班连接,第i次航班详细连接了ui国和vi国,如果Victor飞越这些国家,需要消耗Victor的飞机的燃油。他有可能从第一个国家飞到每一个国家。
Victor现在在一个编号为1的国家,他想知道最少需要多少燃料才能让他至少访问每个国家一次,最后回到第一个国家。
考虑状压dp,\(f_{s,i}\)表示s这个集合里的点全走并且走到i的最短路长,然后就能写出转移方程
\[f_{s,j}=min(f_{s-(1<<j),k} + dis_{k,j})\ ,\ j,k\in s
\]
\(dis_{j,k}\)就是j到k的最短路
其实就是像弗洛伊德一样枚举一个断点,然后去更新dp值
而我们是从点0出发的(我是从0~n-1存的点),所以一定要保证\(1\in s\)
最后只要取\(min_{i=1}^{n-1}f_{(1<<n)-1,i}+dis_{i,0}\)就好了
时间复杂度没算错的话应该是\(O(2^{n-1}\times n^2)\)
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 16;
using namespace std;
int T,n,m,mp[N + 5][N + 5],f[(1 << N) + 5][N + 5],ans;
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&m);
int u,v,w;
memset(mp,127,sizeof(mp));
memset(f,127,sizeof(f));
ans = mp[0][0];
for (int i = 0;i < n;i++)
mp[i][i] = 0;
for (int i = 1;i <= m;i++)
{
scanf("%d%d%d",&u,&v,&w);
u--,v--;
if (u != v)
mp[v][u] = mp[u][v] = min(mp[u][v],w);
}
for (int i = 0;i < n;i++)
for (int j = 0;j < n;j++)
for (int k = 0;k < n;k++)
if (mp[j][i] < mp[n][n] && mp[i][k] < mp[n][n])
mp[j][k] = min(mp[j][k],mp[j][i] + mp[i][k]);
f[1][0] = 0;
f[0][0] = 0;
for(int i = 3;i < (1 << n);i++)
if (i & 1)
{
for (int j = 1;j < n;j++)
if (i & (1 << j))
for (int k = 0;k < n;k++)
if (k != j && (i & (1 << k)))
f[i][j] = min(f[i][j],f[i - (1 << j)][k] + mp[k][j]);
}
for (int i = 1;i < n;i++)
ans = min(ans,f[(1 << n) - 1][i] + mp[i][0]);
if (n == 1)
ans = 0;
printf("%d\n",ans);
}
return 0;
}