title : 状压DP
date : 2022-3-5
tags : ACM,图论,动态规划
author : Linno


状压DP

状态压缩,是利用二进制数的性质对问题进行优化的一种算法,经常与搜索和DP结合。

状态压缩动态规划,俗称状压DP。在动态转移的过程中用二进制数表示状态,并利用位运算的性质可使得枚举方案大大减少。

前置知识

位运算基础

判断数字x二进制下第i位是不是1。

if(((1<<(i-1))&x)>0)

将数字x二进制下第i位改成1

x=x|(1<<(i-1)) //改成0的情况改成x=x&~(1<<(i-1))

将数字x最右边的1去掉

x=x&(x-1) //也是我们熟知的lowbit

数字x的二进制下1的个数

int count(int x){int res=0;while(x){res++;x&=(x-1);}return res;}

图论应用

状压DP+图论状态转移举例

for(int S=0;S<(1<<n);S++) //可以理解为n个点的选或不选for(int i=0;i<n;i++) if(S&(1<<i))for(int j=0;j<n;j++) //转移点if(!(S&(1<<j))&&G[i][j]) //j不在点集中且i到j有路径dp[j][S|(1<<i)]=min(dp[j][S|(1<<i)],dp[i][S]+G[i][j]);//S表示点集,j是转移结点,G[i][j]是转移代价

生成子集

for(int subs=s&(s-1);subs;subs=s&(subs-1)) //子集,可生成子图
//subs=(subs-1)&s 可以枚举s的子集

有兴趣的同学可以搜一下斯坦纳树。

例题

P1896 [SCOI2005]互不侵犯

模板题:n*n的格子里面放k个国王,国王附近八个格子内没有国王,可以有多少种方案?

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=10;
const int mod=1e9+7;int count(int x){int res=0;while(x){res++;x&=(x-1);}return res;}
int lb(int x){return x&(-x);}int n,K,ans=0,dp[N][(1<<10)][N*N];signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>K;for(int j=0;j<(1<<n);j++){if(j&(j<<1)) continue;dp[1][j][count(j)]=1; } for(int i=2;i<=n;i++){for(int j=0;j<(1<<n);j++){ //枚举子集 if(j&(j<<1)) continue; //左右互不侵犯 for(int k=0;k<(1<<n);k++){ //前一行的状态if(k&(k<<1)) continue;if(!((j&k)||(j&(k<<1))||((j<<1)&k))){ //当前状态满足和上一行的状态互不侵犯 int num=count(j);//,num2=count(k);for(int p=0;p<=K;p++){dp[i][j][p+num]+=dp[i-1][k][p];}}}}}for(int j=0;j<(1<<n);j++){if(j&(j<<1)) continue;ans+=dp[n][j][K];}cout<<ans<<"\n";return 0;
}
luoguP3694 邦邦的大合唱站队

给定n个人m种颜色排成一列,可以抽出k个人将他们以任意顺序插回,问最少k的最小值使得每种颜色的人站在一起。

状态转移方程f[S]=min(f[Sxor(1<<j)]+num[j]−s[r][j]+s[l][j])f[S]=min(f[S\ xor\ (1<<j)]+num[j]-s[r][j]+s[l][j])f[S]=min(f[S xor (1<<j)]+num[j]−s[r][j]+s[l][j])

其中l,rl,rl,r表示最后一个乐队所对应的区间。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=1e5+7;int n,m,a[N],num[25],sm[1<<21];
int sum[N][22],dp[1<<21];void dfs(int x,int s,int b){ //dp初始状态——每种状态中的颜色顺序排好的花费 if(x==m) return;if(b) sm[s|(1<<x)]=sm[s]+num[x+1],dfs(x+1,s|(1<<x),1),dfs(x+1,s|(1<<x),0);else dfs(x+1,s,1),dfs(x+1,s,0);
}signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>m;for(int i=1;i<=n;i++){cin>>a[i];for(int j=1;j<=m;j++) sum[i][j]=sum[i-1][j];sum[i][a[i]]++; //每种颜色的前缀和 num[a[i]]++;}dfs(0,0,0);dfs(0,0,1);memset(dp,inf,sizeof(dp));dp[0]=0;for(int i=1;i<(1<<m);i++){ //枚举状态 for(int j=1;j<=m;j++){ //枚举颜色 if(i&(1<<(j-1))){ int l=sm[i^(1<<j-1)],r=sm[i];dp[i]=min(dp[i],dp[i^(1<<(j-1))]+(r-l)-(sum[r][j]-sum[l][j]));}} }cout<<dp[(1<<m)-1]<<"\n";return 0;
}
[NOI2001] 炮兵阵地

题意:给定N×M的格子,有些地方不能放炮,炮之间不能相互攻击到,问最多能放多少个炮。(炮的攻击范围是上下左右两格以及自己所在格子)。

dp[i][j][k]表示两行状态分别为i和j时的答案,然后第三维用来滚动就ok了。

#include<bits/stdc++.h>
using namespace std;
const int N=1<<10;int n,m,mp[105],f[N][N][2],sum[N],ans=0; //f[i][j]表示前一行是状态i,后一行是状态j的答案,第三位用来滚动
bool g[N];
char ch;int getsum(int x){  //统计1的个数 int res=0;while(x){res+=(x&1);x>>=1;} return res;
}signed main(){cin>>n>>m;int M=(1<<m);for(int i=0;i<n;i++){for(int j=0;j<m;j++){ch=getchar();while(ch!='P'&&ch!='H') ch=getchar();if(ch=='H') mp[i]=mp[i]<<1|1;else mp[i]=mp[i]<<1;} } for(int i=0;i<M;i++) sum[i]=getsum(i);  //预处理 for(int i=0;i<M;i++){for(int j=0;j<M;j++){  //枚举前两行的状态和答案 if((i&j)||(i&mp[0])||(j&mp[1])||(i&(i<<1))||(j&(j<<1))||(i&(i<<2))||(j&(j<<2))) continue;f[i][j][1]=sum[i]+sum[j];}}for(int i=2;i<n;i++){  //枚举行 for(int j=0;j<M;j++){  //枚举第i-1行状态 if((j&mp[i-1])||(j&(j<<1))||(j&(j<<2))) continue;for(int k=0;k<M;k++){  //枚举第i行状态 if((k&(k<<1))||(k&(k<<2))||(k&mp[i])||(k&j)) continue;for(int v=0;v<M;v++){  //枚举第i-2行状态 if(v&(v<<1)||(v&(v<<2))||(v&j)||(v&k)||(v&mp[i-2])) continue;f[j][k][i%2]=max(f[j][k][i%2],f[v][j][(i-1)%2]+sum[k]);}}}}for(int i=0;i<M;i++){for(int j=0;j<M;j++){ans=max(ans,f[i][j][(n-1)%2]);  //取最大值 }}cout<<ans<<"\n";return 0;
}

参考文献

https://www.bilibili.com/video/BV1Z4411x7Kw

https://www.cnblogs.com/ljy-endl/p/11627018.html

【算法竞赛学习笔记】状压DP相关推荐

  1. [学习笔记]状压dp

    状压 \(dp\) 1.[SDOI2009]Bill的挑战 \(f[i][j]\) 表示匹配到字符串的第 \(i\) 位状态为 \(j\) 的方案数 那么方程就很明显了,每次枚举第 \(i\) 位的字 ...

  2. 【算法竞赛学习笔记】超好懂的斯坦纳树详解!!!

    title : 斯坦纳树 tags : ACM 图论 date : 2021-6-26 author : Linno 什么是斯坦纳树 给定 n 个点 A1,A2,⋯,An试求连接此n个点,总长最短的直 ...

  3. 【算法竞赛学习笔记】pb_ds-超好懂的数据结构

    title : pb_ds date : 2021-8-21 tags : ACM,数据结构 author : Linno 简介 pb_ds库全称Policy-Based Data Structure ...

  4. 【算法竞赛学习笔记】快速傅里叶变换FFT-数学提高计划

    tilte : 快速傅里叶变换FFT学习笔记 tags : ACM,数论 date : 2021-7-18 简介 FFT(Fast Fourier Transformation),中文名快速傅里叶变换 ...

  5. 【算法竞赛学习笔记】后缀自动机SAM-超经典的字符串问题详解

    title : 后缀自动机 date : 2021-11-11 tags : ACM,字符串 author : Linno 前置知识 KMP,Trie,AC自动机等字符串基础 DFA(有限状态自动机) ...

  6. 【算法竞赛学习笔记】KD-Tree

    title : KD-Tree date : 2022-4-7 tags : ACM,数据结构 author : Linno K-D tree K-D树是在k维欧几里得空间中组织点的数据结构.在算法竞 ...

  7. 【算法竞赛学习笔记】莫队算法-超优雅的暴力算法

    title : 莫队算法 tags : ACM,暴力 date : 2021-10-30 author : Linno 普通莫队 常用操作:分块/排序/卡常/离散化等,直接上板子. luoguP270 ...

  8. 【算法竞赛学习笔记】Link-Cut-Tree基础-超好懂的数据结构

    titile : Link-Cut-Tree time : 2021-7-21 tags : ACM,数据结构 author : Linno LCT Link-Cut-Tree,中文名为动态树,是一种 ...

  9. 【算法竞赛学习笔记】离散对数与BSGS-数学提升计划

    title : 离散对数与BSGS date : 2021-8-12 tags : ACM,数论 author Linno 阶 对与m互质的整数a,我们记满足an≡1modma^n\equiv 1\m ...

最新文章

  1. php提交表单关闭弹出层,使用js实现关闭js弹出层的窗口
  2. Cron 表达式极速参考
  3. Hadoop学习笔记(1) ——菜鸟入门
  4. Sublime Text3配置Lua运行环境
  5. Office 365系列(3)------Office 365认证使用ADFS安装部署参考
  6. window 下内存泄漏检测
  7. 为啥学java要看那么多东西_编程语言那么多,为啥学Java的人那么多?
  8. rj45管脚定义_rj45接口定义,rj45插座引脚定义
  9. 每日英语:How to say No to other people
  10. java hibernate注解_Hibernate注解方法使用总结
  11. ubantu卸载MySQL数据库
  12. 宽带可调飞秒激光器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  13. python实现多表合并_python实现excel多表合并的方法
  14. VS2017下搭建OPEN CASCADE
  15. ROS机器人操作系统——订阅者Subscriber的编程实现(五)
  16. 水泥路面、桥梁基建、隧道裂痕裂缝检测数据集
  17. C++程序设计作业--坦克大战[分享]
  18. 计算机分级时无法度量视频播放,无法度量视频播放性能怎么办-无法度量视频播放性能的解决方法 - 河东软件园...
  19. 王者荣耀有史以来被削废的英雄盘点:还记得那个超级兵吗?
  20. tif批量转png格式 python

热门文章

  1. el-input/input取消浏览器记住密码问题
  2. iOS 图标上的数字
  3. L3-python语言中的几种特征操作
  4. python输出最长字符串_使用Python打印最长的字母子字符串,并打结...
  5. python爬虫实战之多线程爬取前程无忧简历
  6. virtualbox 虚拟机里支持 USB3.0 , 无线网卡
  7. JAVA中break和continue用法
  8. python邮件发送
  9. 硬盘健康状态、温度、通电次数、写入量和序列号检查软件
  10. 去叶剂行业调研报告 - 市场现状分析与发展前景预测