维克多与世界
时间限制:4000/2000 MS(Java /其他)内存限制:262144/131072 K(Java /其他)
提交总数:2666接受提交:1201
问题描述
经过多年的努力,Victor终于获得了飞行员执照。为了庆祝,他打算给自己买飞机,环游世界。地球上有n个国家,编号从1到n。它们通过m个无向航班进行连接,详细地说,第i个航班将连接ui-th和vi-th国家,如果Victor飞过它,这将花费Victor的飞机加油。而且他有可能从第一国家飞往每个国家。
维克多现在在一个数字为1的国家/地区,他想知道最少需要燃料才能使他至少一次访问每个国家并最终返回第一个国家。
输入值
输入的第一行包含一个整数T,表示测试用例的数量。
在每个测试案例中,第一行都有两个整数n和m,分别表示国家/地区和航班的数量。
然后有m行,每行包含三个整数ui,vi和wi,描述了一次飞行。
1≤T≤20。
1≤n≤16。
1≤m≤100000。
1≤wi≤100。
1≤ui,vi≤n。
输出量
您的程序应打印出T行:第i行应包含一个整数,表示Victor完成行程所需的最少燃料量。
样本输入
1个
3 2
1 2 2
1 3 3
样本输出
10
#include<cstdio>
#include<cstring>
#include<algorithm>
const int INF = 1<<30 ;
int dp[1<<17][20];
int n,m,u,v,w,mp[20][20] ;
void floyd()
{
//最开始只允许经过 0号顶点进行中转,接下来只允许经过0号和1号顶点进行中转......允许经过0~n-1号所有顶点进行中转,来不断动态更新任意两点之间的最短路程。即求从i号顶点到j号顶点只经过前k号点的最短路程。
for(int e=0; e<n; e++)//用来中转的点
for(int i=0; i<n; i++)
if(i!=e&&mp[i][e]!=INF)
{
for(int j=0; j<n; j++)
if(mp[i][j]>mp[i][e]+mp[e][j])
mp[i][j] = mp[i][e]+mp[e][j];
}
}
int main()
{
int T ;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=0; i<(1<<n); i++)
for(int j=0; j<=n; j++)
dp[i][j] = INF;
for(int i=0; i<=n; i++)
{
for(int j=0; j<=n; j++)
mp[i][j] = INF;
mp[i][i]=0;
}
while(m--)
{
scanf("%d%d%d",&u,&v,&w);
u--; v--;
if(mp[u][v]>w)
mp[u][v]=mp[v][u]=w;
}
floyd();
dp[1][0] = 0 ;
for(int s=1; s<(1<<n); s++)//s 已经做过的城市的集合走过1 3 4->10110
for(int i=0; i<n; i++)//i停留在i,从i走到j有n种法,也要枚举一遍
if(dp[s][i]!=INF)
{
for(int j=0; j<n; j++)
dp[s|(1<<j)][j] =min(dp[s][i]+mp[i][j],dp[s|(1<<j)][j]) ;
}
int ans = INF;
for(int i=0; i<n; i++)
ans = min(dp[(1<<n)-1][i]+mp[i][0],ans) ;
printf("%d\n",ans);
}
}