专栏文章

状压DP-学习笔记

算法·理论参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mipmj6g8
此快照首次捕获于
2025/12/03 14:26
3 个月前
此快照最后确认于
2025/12/03 14:26
3 个月前
查看原文

状压介绍:

核心思想:

状态压缩,顾名思义,把一个复杂度状态压缩,使其用一个二进制数表示。
比如选数,二进制数10010 表示第二个和第五个数字被选上。
一般我们使用位运算来处理相关操作。

定义状态:

一般情况下,状态压缩有一维是二进制,表示当前的选择方案,对于某些问题,需要有顺序,那么第一位会记录当前选到了哪个。
比如 dp[2][1] 表示目前选到第二个数了,其中第一个被选中。

循环:

拿二维来说,定义一个二维循环:
CPP
	for(int i=1;i<=n;i++)
		for(int j=0;j<(1<<n);j++)
其中第一层,i表示当前遍历到的编号(由1到n),而内层的 j 表示当前选择的方案,如当前 j4时,此时转化为二进制数 100 表示此时方案中只有第3个数被选上,由于对于 nn 个数而言,有 n2n^2 种方案,因此j0 (都不选)循环到 (1<<n)(1<<n) (全选),当然,这还是需要按照实际情况调整的。
对于无序的题目,定义一维,遍历 j 即可。

判断、操作方式:

对于状压DP,我们需要进行一些处理和操作:
以如下代码为例:
CPP
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
double x[221],y[120],ans=1e10;
double dp[21][41000];
double dist(int a,int b){
	return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>x[i]>>y[i];
	}
	memset(dp,0x7f,sizeof(dp));
	for(int k=1;k<=(1<<n)-1;k++){
		for(int i=1;i<=n;i++){
			if(k&(1<<i-1)==0) continue;
			if(k==(1<<i-1)){
				dp[i][k]=0;
				continue;
			}
			for(int j=1;j<=n;j++){
				if((k&(1<<(j-1)))==0||i==j) continue;
				dp[i][k]=min(dp[i][k],dp[j][k-(1<<i-1)]+dist(i,j));
			}
		}
	}
	for(int i=1;i<=n;i++){
		ans=min(ans,dp[i][(1<<n)-1]+dist(i,0));
	}
	printf("%.2f",ans);
    return 0;
}


k & (1 << (i - 1)) == 0判重
k == (1 << (i - 1))判断边界条件
(k & (1 << (j - 1))) == 0 || i == j 避免重复访问
这些操作的意义十分明了,所以可以根据需要来自我处理。

状压的使用场景:

  • 1.有关子集等需要枚举全部子集方案。
  • 2.求排列组合的方案。
  • 3.棋盘防止等相关问题。
  • 4.图上路径问题。

使用状压的标志:

nn 较小、求最大最小值或方案数。

评论

0 条评论,欢迎与作者交流。

正在加载评论...