题目链接:传送门
题意:
n个城市m条路,刚开始在点1,求把每个城市都遍历一边最后回到1的花费的最小值。
分析:
我们首先需要预处理出任意两个国家之间的最短距离,因为数据范围很小,所以直接用Floyd就行了。之后,我们用f[S][i]表示访问国家的情况为S,当前最后访问的一个国家是i所需要的最小总油量,其中,S的二进制表示记录了访问国家的情况,S在二进制表示下的第i位(不管是从左往右还是从右往左都可以)如果是1则表示第i个国家被访问过了,否则表示第i个国家没有被访问过,那么f[S|(1<<i)][i]=min(f[S][j]+f[i][j]),其中i和j满足S&(1<<j)=1且S&(1<<i)=0。最开始时,除了f[1][1]是0,其他情况都是无穷大,之后先枚举S,再枚举i(我验题的时候因为这里搞反结果WA了),那么最终的答案就是min(f[(1<<n)-1][i]+f[i][1]),其中i∈ [2,n]。总复杂度为O(n3+n2∗2n)。
转自Bestcode。
下面说说我的状态转移,首先也处理好了每两个城市之间的最短路。
然后DP[S][J]S转换成二进制后1代表去过,0代表没有去过最后留在J的最小花费,然后就枚举S没有去过的城市k
DP[S|(1<<k)][k]=min(DP[S][j]+mp[j][k])
代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 18;
const int inf = 0x3f3f3f3f;
int dp[1<<maxn][maxn];
int mp[maxn][maxn];
void init(){
memset(dp,inf,sizeof(dp));
for(int i=0;i<maxn;i++)
for(int j=0;j<maxn;j++)
mp[i][j]=inf;
}
int main()
{
int t,n,m;
scanf("%d",&t);
while(t--){
init();
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
mp[u][v]=min(mp[u][v],w);
mp[v][u]=min(mp[v][u],w);
}
for(int i=0;i<maxn;i++) mp[i][i]=0;
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]);
}
}
}
dp[1][1]=0;
for(int i=0;i<(1<<n);i++){
for(int j=0;j<n;j++){
if(!(i&(1<<j))){
for(int k=1;k<=n;k++){
dp[i|(1<<j)][j+1]=min(dp[i|(1<<j)][j+1],dp[i][k]+mp[k][j+1]);
}
}
}
}
int ans = inf;
for(int i=1;i<=n;i++)
ans = min(ans,dp[(1<<n)-1][i]+mp[i][1]);
printf("%d\n",ans);
}
return 0;
}