题目为带权无环图,要将每个点都走恰好一遍,问最短路径-----为旅行商问题
那要怎么搞呢,看了别人的题解发现,其无非就只有两个关键点:
1.一个为当前走过了哪些点
2. 目前是哪个点 ( f[ i ][ j ] )
走过了哪些点,n最大为20,不难想到可以用二进制的位来模拟,1则走过,0则还没走,关键在于转移:考虑上一步?哦吼,走了哪个点?那是不是就是 i 中 二进制表示下为1的位都有可能为上一个点 ?那现在又准备走那步勒?不就是i 中 二进制表示下为0的位都有可能为当前准备走的点???
那不就出来拉~
if ( ( i >> j ) & 1) // 用 j 表示找到的上一步的点
if ( ~(i >> k) & 1) // 用k表示目前看上准备走的点
转移不就是 :
f [i + ( 1 << k ) ] [ k ] = min( f[ i + ( 1 << k ) ] [ k ] ,f[ i ][ j ] + dis [ j ][ k ] )
// f[ i + ( 1<<k ) ] 看不懂?不会吧,你不会认为傻傻地在原地不动就能改变什么吧,肯定走了就要把k那位赋值为1啊,默默的付出是没啥人会注意的,要大声说出来兄弟
还有要 注意看代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 21,M = 1<<21;
int f[M][N],dis[N][N];
//f[i][j] i表示走过了哪些路,j表示最后一步是哪
int main()
{
int n;
scanf("%d",&n);
memset(f,0x3f,sizeof(f));//初始化类似于正无穷大10^9
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
scanf("%d",&dis[i][j]);
}
}
f[1][0] = 0;//表示初始化时只有一个数走到自己为0,防循环不进
for (int i = 0; i < 1<<n; i++){
for (int j = 0; j < n; j++){
if((i>>j)&1)//j已经被走过
{//假定j为上一步
for (int k = 0; k < n; k++){
if(~(i>>k)&1)//这步还没人走
{//指定为最后一步
f[i+(1<<k)][k] = min(f[i+(1<<k)][k],f[i][j]+dis[j][k]);
}
}
}
}
}
cout<<f[(1<<n)-1][n-1];
return 0;
}