状态压缩动态规划

状态压缩动态规划是通过整数的二进制位来表示集合状态的一种方法,尤其适用于集合元素数量较少的情况。本文介绍了如何枚举所有状态和子集,并通过Atcoder ABC187F题目的例子详细阐述了状态压缩DP的应用,包括状态设计、初值设定和状态转移方程。
摘要由CSDN通过智能技术生成

状态压缩类动态规划,顾名思义,其核心就是在于对状态表达进行压缩。

在动态规划中,重点、难点更是突破点所在,就是阶段划分过后的状态表达及转移。在一般动态规划中,如果要维护一个集合的状态,常常用int 或bool类型的数组进行表达与描述。如果这个集合包含的元素个数很少,我们就把描述集合状态的数组压缩到一个整数中,这种用一个整数来描述一个集合的方法就叫作“状态压缩”。而当状态的某个维度存储的是一个集合的状态,这类问题就称作:状态压缩动态规划。

如果集合中每个元素的状态只有两种,我们一般用1和0进行描述,因此当我们用一个整数表达时,就自然而然地与二进制相对应。也就是说我们可以用一个整数对应的二进制位来表示集合中元素的状态。(如果状态有三种就采用三进制,以此类推)

比如给定一个整数23,它所对应的的二进制数为10111,那么我们就可以认为这个集合中包含了第1、2、3、5这些元素,而不包括第4个元素。

由此,我们在对整数的二进制位进行操作时,需要熟练掌握位运算的相关知识。这里给出状态压缩中常用的几个表达式:

判断一个数字x二进制第i位是否为1 
	mask&(1<<i)>0 
将一个数字x二进制第i位更改为1
	x|=(1<<i)
将一个数字x二进制第i位更改为0 
	x=x^(1<<i)
把一个数字二进制下最靠右的第一个1去掉
	x=x&(x-1) 
mask1是mask2的子集
	(mask1&mask2)==mask1
n个位置所占的位数
	(1<<n)-1

        这里注意:位运算的运算级别非常低(甚至低于“==”),所以在使用位运算时可以说能加括号就加括号,不管括号是不是多余

状态操作

在进行状态压缩动态规划时,其基本思路同普通的DP没有太大区别,所以在进行状态转移时也要枚举各个状态,这就涉及到了对二进制表示状态时的操作(如对不同状态的枚举、遍历,子集枚举等)

1、枚举所有状态

for(int mask=1;mask<(1<<n);mask++)
{
    
}

2、枚举子集

        以11(1011)为例:

int mask=11;//1011
for(int i=mask;i;i=(i-1)&mask)
{
	for(int j=3;j>=0;j--)//11的二进制位有4位,所以从3—0枚举所有二进制位
	{
		if((i>>j)&1) cout<<1;
		else cout<<0;	
	} 
	cout<<endl;
}

        证明i=(i-1)&mask:        

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值