思路
一个典型的状态压缩问题,状态压缩核心的思想就是把一个要用集合表示的状态用一个二进制的数来表示,
这里要求所有路的最短路径,状态怎么表示呢?
首先如果一条路径的起点和终点确定了,经过了那几个点也确定了,权值知道,那么就可以表示一条路径了,所以我们用f[i][j]表示状态,其中i表示了所有点的状态,也就是是否经过了,j表示终点,因为起点都是0,所以我们只要记录终点就可以了。
状态压缩:如果要表示一群点的状态的话,首相想到的数据结构是集合,但是这里可以用状态压缩,因为一个点的状态只有两个,经过和没经过,所有每一个点的状态都可以用一个二进制位来表示那么一共最多有20个点,那么我们就可以用一个20位的二进制数来表示所有点的状态了。
状态转移:f[i][j]怎么转移呢?,一个点肯定是由前一个点过来的,所以f[i][j]=min(f[i][j],f[s][k]),其中k为上一个终点,s为上一个终点的状态,当然是有条件的,s中在j位上一定是0,在k上一定是1。
步骤:
1.存下所有的权w[i][j];
(其中初始化 f[1][0]=0,因为终点为0时距离为0,而且状态为1)
2.枚举所有的状态(循环一)
3.枚举所有的终点(循环二)
4.枚举所有终点的前一个点(循环三)
代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=20;
int n;
int f[1<<N][N];
int w[N][N];
int main(){
cin >> n;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++){
cin >> w[i][j];
}
}
memset(f,0x3f,sizeof(f));
f[1][0]=0;
for(int i=0;i<1<<n;i++){
for(int j=0;j<n;j++){
if(i>>j & 1){
for(int k=0;k<n;k++){
if(i >> k&1)
{
f[i][j]=min(f[i][j],f[i-(1<<j)][k]+w[k][j]);
}
}
}
}
}
cout << f[(1<<n)-1][n-1];
}