状压DP详解(位运算)
前言:
状压DP是一种非常暴力的做法(有一些可以排除某些状态的除外),例如dp[S][v]中,S可以代表已经访问过的顶点的集合,v可以代表当前所在的顶点为v。S代表的就是一种状态(二进制表示),比如 (11001)2 代表在二进制中{0,3,4}三个顶点已经访问过了,(11001)2 代表的十进制数就是25 ,所以当S为25的时候其实就是代表已经访问过了{0,3,4}三个顶点,那假如一共有5个顶点(标号为01234)的话,所有的顶点都访问完毕应该S为什么呢?是 (11111)2。
那么,在状态转移的过程中我们势必要对十进制数转化为二进制数的每一个的位数进行一些操作。下面,列举了一些位运算的操作。

对于上面的一些操作举例一些
就如之上的 (11001)2 来说是已经访问了0,3,4三个顶点,当我接下去要访问顶点2的话可以有以下操作:1.看看第3位是什么?那么选择取出二进制数x的第k位,y = x >>(k-1) & 1,y = (110)2 & 1 = 0,2.把第3位变为1,y = x | (1<<(k-1)),y = (11001)2 | (100)2 = (11101)2,所以顶点2访问完毕,加入到集合S中也结束了,此时S为(11101)2 = 29。
接下去就可以开始讲一些例题了。最经典的就是白皮书上的tsp问题。
tsp问题
题意:假设有一个旅行商人要拜访N个城市,他必须选择所要走的路径,路径的限制是每个城市 只能拜访一次,而且最后要回到原来出发的城市,要求路径的总和最小。其中,2<=N<=15
思路:首先我们试着去设计状态,假设现在已经访问过的顶点的集合为S,当前所在顶点为v, 用dp[S][v]表示从v出发还有访问剩余的所有顶点,最终返回到顶点0的路径总和最小值。从v出发可以到任意一个还未访问过的顶点,边界就是访问完所有顶点并且返回到顶点0。
递推式为
dp[V][0] = 0;(边界)
dp[S][v] = min{dp[S∪{u}][u] + d[u][v] | u不属于S}
先通过记忆化搜索来看一下code
// s: 已经访问过的节点状态 v: 出发位置
int dfs(int s, int v)
{
if(dp[s][v]>=)
return dp[s][v];
if(s==(<<n)- && v==) //已经访问过所有的节点并且再次回到了0起点
return dp[s][v]=;
int ans=INF;
for(int u=;u<n;u++)
if(!(s>>(u &))) // 如果u这位是0(代表没有走过的话)就走u这个节点
ans=min(ans, dfs(s | (<<u), u)+mp[v][u]); //dfs中第一个参数表示第u位变成1后的二进制数
return dp[s][v]=ans;
}
int main()
{
memset(dp, -, sizeof(dp));
printf("%d\n", dfs(, ));
return ;
}
但是在这个问题中,对于任意两个整数i和j,如果他们对应的集合满足S(i)包含于S(j),就有i<=j,因此我们可以写成循环的方式。
int dp[<<maxn][maxn]; void solve()
{
for(int S = ; S < <<n; S++)
{
fill(dp[S],dp[S]+n,INF); //初始化
}
dp[(<<n) - ][] = ; //边界条件
//之前的记忆化搜索转换为递推式
for(int S = (<<n) - ; S >= ; S--)
{
for(int v = ; v < n; v++)
{
for(int u = ; u < n; u++)
{
if(!(S>>u&)){ //如果u不属于S集合
dp[S][v] = min(dp[S][v],dp[S|<<u][u]+d[v][u]);
}
}
}
}
printf("%d\n",dp[][]);
}
这样子,一个状压DP的板子差不多就出来了。
状压DP详解(位运算)的更多相关文章
- 状态压缩dp 状压dp 详解
说到状压dp,一般和二进制少不了关系(还常和博弈论结合起来考,这个坑我挖了还没填qwq),二进制是个好东西啊,所以二进制的各种运算是前置知识,不了解的话走下面链接进百度百科 https://baike ...
- 状压DP详解+题目
介绍 状压dp其实就是将状态压缩成2进制来保存 其特征就是看起来有点像搜索,每个格子的状态只有1或0 ,是另一类非常典型的动态规划 举个例子:有一个大小为n*n的农田,我们可以在任意处种田,现在来描述 ...
- 状态压缩动态规划(状压DP)详解
0 引子 不要999,也不要888,只要288,只要288,状压DP带回家.你买不了上当,买不了欺骗.它可以当搜索,也可以卡常数,还可以装B,方式多样,随心搭配,自由多变,一定符合你的口味! 在计算机 ...
- Codeforces 895C - Square Subsets 状压DP
题意: 给了n个数,要求有几个子集使子集中元素的和为一个数的平方. 题解: 因为每个数都可以分解为质数的乘积,所有的数都小于70,所以在小于70的数中一共只有19个质数.可以使用状压DP,每一位上0表 ...
- luoguP6622 [省选联考 2020 A/B 卷] 信号传递(状压dp)
luoguP6622 [省选联考 2020 A/B 卷] 信号传递(状压dp) Luogu 题外话: 我可能是傻逼, 但不管我是不是傻逼, 我永远单挑出题人. 题解时间 看数据范围可以确定状压dp. ...
- 状压DP入门详解+题目推荐
在动态规划的题型中,一般叫什么DP就是怎么DP,状压DP也不例外 所谓状态压缩,一般是通过用01串表示状态,充分利用二进制数的特性,简化计算难度.举个例子,在棋盘上摆放棋子的题目中,我们可以用1表示当 ...
- 状压DP初探·总结
2018过农历新年这几天,学了一下状态压缩动态规划,现在先总结一下. 状态压缩其实是一种并没有改变dp本质的优化方法,阶段还是要照分,状态还是老样子,决策依旧要做,转移方程还是得列,最优还是最优, ...
- 状压入门--bzoj1087: [SCOI2005]互不侵犯King【状压dp】
Description 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上 左下右上右下八个方向上附近的各一个格子,共8个格子. Input 只有一行, ...
- 状压DP : [USACO06NOV]玉米田
玉米田 内存限制:128 MiB 时间限制:1000 ms 标准输入输出 题目类型:传统 评测方式:文本比较 题目描述 农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ ...
随机推荐
- 从零开始的全栈工程师——js篇(作用域 this 原型笔试题练习)
作用域 // 1. fn() function fn () { console.log(12) } var as = function () { console.log(45) } // 2. var ...
- Java方法命名之“由简入繁”原则
1.访问控制层(Controller 层)中的方法命名方向是简洁明了,向着自然化语言方向靠拢,比如“更新用户”,建议命名为“updateUser”,而非“updateUserById”,实际上我们更新 ...
- aliyun maven repository
<mirrors> <mirror> <id>alimaven</id> <name>aliyun maven</name> & ...
- vue监听input标签的value值方法
<input id="materialSearch" type="text" @keyup.enter="search" @input ...
- input输入框不能获得焦点
今天在ipad上遇到一个问题:jquery 调用 $(id).focus() 方法,失效,不能弹出键盘获得输入的焦点. 开始以为是 $(id).focus() 方法的问题,然后就试着用原声的docum ...
- [原创] Debian9上配置Samba
Samba概述 Samba是一套使用SMB(Server Message Block)协议的应用程序,通过支持这个协议,Samba允许Linux服务器与Windows系统之间进行通信,使跨平台的互访成 ...
- mysql:用cmd启动mysql服务被拒绝原因
原因是命令行的权限不够,需要以管理员模式运行,然后输入net start mysql 即可启动mysql服务
- PHP:substr和mb_substr的区别
substr和mb_substr函数都是获取字符串中的某个部分 那么,它们的区别在哪儿呢? 区别: substr :全部是英语.数字就正常:但有一些的字元是占用多个位元的,substr()就得不到你预 ...
- SINAMICS S120屏蔽报警
通用的报警屏蔽方法: P2118 = 需要屏蔽的报警号 P2119 = 屏蔽的方式
- Selenium入门18 断言
自动化测试需对比实际结果与预期结果,给出测试结论. 1 条件判断 if ...else... 2 assert ... #coding:utf-8 #断言 from selenium import w ...