状压dp学习笔记

状压dp

学习这个之前,首先做一道题;

HDU-5418

Victor and World

After trying hard for many years, Victor has finally received a pilot license. To have a celebration, he intends to buy himself an airplane and fly around the world. There are n countries on the earth, which are numbered from 1 to n. They are connected by m undirected flights, detailedly the i-th flight connects the ui-th and the vi-th country, and it will cost Victor’s airplane wi L fuel if Victor flies through it. And it is possible for him to fly to every country from the first country.

Victor now is at the country whose number is 1, he wants to know the minimal amount of fuel for him to visit every country at least once and finally return to the first country.


题目大意为从 1 号点出发,要求最少遍历一次其他点,然后又回到 1 号点的最少花费;

这道题在没学状压dp前可能会往最短路上面去想,但是在我所学的几种最短路算法里面,没有一种是可以规定必须经过几号点的,而且就算可以也会十分麻烦;

所以当看到n<=16时,可以往状压dp方面想想;

先推出式子:

dp[i][s|(1<

解释:

dp[i][s]表示的是经过了 s 个点在当前 i 点的最少花费;

f[i][j]表示的是 i 到 j 的最短路,不一定要 i 和 j 直接相连;

s 表示的不是点的个数,而是把 s 拆成二进制,当二进制位为 1 时,表示这个二进制位在集合里面,所以二进制位可以代表顶点的标号,当某个二进制位为 1 时,代表这个顶点在集合中,这也是状压dp的精髓所在;但是这有个明显缺点就是点的总数 n 不能过大;

s|(1<

剩下来的就是怎么枚举了,这个枚举类似于二进制枚举,也就是利用二进制枚举找子集那个方法;

先把这道题的代码贴下,比较模板;

#include
#define ll long long
#define pa pair
#define lson k<<1
#define rson k<<1|1
#define inf 0x3f3f3f3f
//ios::sync_with_stdio(false);
using namespace std;
const int N=100100;
const int M=1000100;
int ma[20][20];//两点之间的最小距离 
int dp[20][N];
int main(){
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--){
    	memset(ma,inf,sizeof(ma));
    	int n,m;
    	cin>>n>>m;
    	for(int i=1;i<=m;i++){
    		int p,q,w;
    		cin>>p>>q>>w;
    		ma[p][q]=min(ma[p][q],w);
    		ma[q][p]=min(ma[q][p],w);
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				for(int k=1;k<=n;k++){
					ma[i][j]=min(ma[i][j],ma[i][k]+ma[k][j]);
				}
			}
		}
		memset(dp,inf,sizeof(dp));
		dp[1][1]=0;
		for(int s=1;s<(1<<n);s++){
			for(int i=1;i<=n;i++){
				if(s&(1<<(i-1))){
					for(int j=1;j<=n;j++){
						if(!(s&(1<<(j-1)))&&ma[i][j]){
							dp[j][s|(1<<(j-1))]=min(dp[j][s|(1<<(j-1))],dp[i][s]+ma[i][j]);
						}
					}
				}
			}
		}
		int ans=dp[1][(1<<n)-1];
		for(int i=2;i<=n;i++){
			ans=min(ans,dp[i][(1<<n)-1]+ma[i][1]);
		}
		cout<<ans<<endl;
	}
    return 0;
}

你可能感兴趣的:(#,状压dp)