最短Hamilton路(二进制状态压缩)

二进制状态压缩

二进制数的最低位从0开始计数
操作 | 实现
—|—
取出第k位 | $(n>>k)&1$
取出第0-(k-1)位 | n& ( (1<<K)-1 )
n的二进制数第k位取反 | n^(1<<K)
n的二进制数第k位赋值为1 | n|(1<<k)
n的二进制数第k位赋值为0 | n&(~1<<K)

状态压缩dp例题

最短Hamilton路

给定n个顶点的带权无向图(n<=20),顶点从0-(n-1)编号,求最短的Hamilton路

Hamilton路定义:0-(n-1)每个点不重不漏恰好经历一次

思路

  • 数i的二进制每一位代表点i是否走过,1代表走过,0代表还未走过
  • F[i,j]代表点被经历的状态,i是二进制数,处于点j时的最短路径和
  • 状态转移:F[i,j]=min{F[i xor (1<<j) , k ] + weight[k,j] }
    xor代表异或
    到点j的距离=所有已经走过的点k路径和+ 点k到点j的权值 的最小值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<iostream>
#include<string.h>
using namespace std;
int weight[20][20];
int f[1<<20][20];//点的编号(0~19)
int main(){
int n;
cin>>n;
memset(f,0x3f,sizeof f);
memset(weight,0,sizeof weight);//点到该点自身 权值为0
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
cin>>weight[i][j];
weight[j][i]=weight[i][j];//无向图
}
}
f[1][0]=0;//从0点开始
for(int i=1;i<(1<<n);i++){//数i代表每一步所有点是否遍历过的状态
for(int j=0;j<n;j++){//j代表当前状态的所在的点
if( (i>>j)&1 == 1){//如果j点已经走过
for(int k=0;k<n;k++){//找到j上一次遍历过的点
if( (i^(1<<j))>>k & 1)//第j位取反(因为上一步没有j点还没走到) k位为1则k位走过
f[i][j]=min(f[i][j],f[i^(1<<j)][k]+weight[k][j]);//更新到j点的最短距离
}
}
}
}
cout<< f[(1<<n)-1][n-1];
return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!