状态压缩dp学习笔记

来自一个菜鸡的总结。

先说说什么是状态压缩dp吧。

状态压缩dp的定义

1.Definition:状态压缩dp的是一种对dp表示形式优化的dp。

说得通俗点,就是把dp数组的维度降低。

而我们举一个具体的例子看看:Vjudge.4124

这道题目看了之后,可以发现是一个明显的汉密尔顿环问题

科普一下,汉密尔顿环问题就是一种求不重复地经过所有点的问题。

而这仍然是一个NP难题,我们用一个 \(O(2^n\times n^2)\) 的算法解决它。

其实有一个非常朴素的思路:

开一个16维的dp数组,一维记录每一个岛屿有没有走过!

时间复杂度上没有问题。

但是,你觉得这样打起来真的舒服吗?这就变成琪露诺的冰雪小屋了(((

所以,状态压缩就出场啦!

我们用一个二进制数来记录这个有没有走过这些岛屿。

而思路已经清楚了,我们来想想具体实现。

不过先总结一下状压dp的一些东西。

2.Features:[经验之谈了属于是]

(1)有两种状态的表述(有的题也许有三种);

(2)数据范围一般较小,大概十几;

(3)求解目标为方案数或者极值。//dp的feature啦。

3.常用的位操作:

这个非常非常重要!

(下次打上来,我有点懒)

板子题的实现

于是这个海贼王的什么什么就可以实现粗来了。

这里以卡到85ms的代码为例吧。

1.状态:定义 \(dp[i][j]\) 表示走到 \(j\) 点时,并且点的访问情况为二进制的 \(i\) 的最少花费。

2.状态转移:[伪代码]

for(循环断点k){
    if(如果j在i中的状态为0,即没去过)continue;
    dp[i][j]=min(dp[i][j],dp[j没有走过时的二进制状态][k]+w[k][j]);
}

3.初始值

dp[1][0]=0;

此时我们假定从第0位用起。-->节省空间

上卡过常的代码:

#include<bits/stdc++.h>
#define ll long long
#define ri register int
using namespace std;
const int MAXN=17;
int n,w[MAXN][MAXN],dp[(1<<MAXN)-1][MAXN];

int main() {
	ios::sync_with_stdio(false);
	memset(dp,0x3f,sizeof(dp));
	cin>>n;
	for(ri i=0;i<n;i++){
		for(ri j=0;j<n;j++){
			cin>>w[i][j];
		}
	}
	
	dp[1][0]=0;
	for(ri i=1;i<=(1<<n)-1;i+=2){//优化
		for(ri j=1;j<n;j++){
			if(((i>>j)&1)==0)continue;//排除不合法状态
			for(ri k=0;k<n;k++){
				if(((i>>k)&1)==0)continue;
				dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+w[k][j]);
			}
		}
	}
	cout<<dp[(1<<n)-1][n-1];
	return 0;
}

差不多得了.jpg

经典的题

1.P1171 售货员的难题

这道题没有什么难点吧,上一道题稍微改一下就OK了。

往返也就加一个 \(w[i][0]\) 就行了。

上一篇:Java流程控制语句2


下一篇:提高代码质量