【学习笔记】状压DP

news/2025/1/15 14:09:41/文章来源:https://www.cnblogs.com/GSNforces/p/18410887

状态压缩DP

对于一个集合,他一有\(2^n\)个子集,而状态压缩就是枚举这些子集,每一个状态就是一个由\(01\)构成的集合,如果为\(0\)就表示不选当前的元素,否则就表示选。因为状态压缩将每一个状态压缩成了一个用二进制表示的数,所以不光可以节省空间,还可以节省时间。
因为是枚举子集,所以时间复杂度为\(O(2^n)\),一般使用的标志就是\(n\le20\).

关灯问题

这是一道经典的\(bfs\)加状态压缩的题目!!

思路

题目要求最多按多少次可以将所有的灯关闭。我们可以将题目给出的条件转化为一个图。每个节点所表示的是当前所有灯的开关情况,但是我们发现这样每个节点的信息要用一个一维数组表示,不好写代码,所以我们可以用状态压缩将每次的开关灯状态转化为一个二进制表示,接着跑一遍\(bfs\)
对于每种状态,枚举接下来按的按钮,生成新一个的状态,如果当前状态没有被访问过,就加进队列,并记录步数,因为是\(bfs\),所以此步数一定是到达当前状态的最小步数。

位运算

题目中涉及到第\(i\)个开关对\(j\)盏灯的影响,设当前状态为\(v\)
1.如果\(a[i][j]=1\)并且第\(j\)盏灯是开着的

\[v=v\oplus(1<<(j-1)) \]

2.如果\(a[i][j]=0\),不用管
3.如果\(a[i][j]=0\)并且第\(j\)盏灯是关着的

\[v=v\oplus(1<<(j-1)) \]

\(code\)

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[110][11],d[1<<11];
queue<int>q;
bool vis[1<<11];
void bfs(){q.push((1<<n)-1);vis[(1<<n)-1]=1;d[(1<<n)-1]=0;while(!q.empty()){int u=q.front();q.pop();for(int i=1;i<=m;i++){int v=u;for(int j=1;j<=n;j++){if(a[i][j]&&(v&(1<<(j-1))))v=(v^(1<<(j-1)));else if(a[i][j]==-1&&(!(v&(1<<(j-1)))))v=(v^(1<<(j-1)));}if(!vis[v]){vis[v]=1;q.push(v);d[v]=d[u]+1;if(v==0)return;}}}
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>m;for(int i=1;i<=m;i++){for(int j=1;j<=n;j++){cin>>a[i][j];}}bfs();if(d[0])cout<<d[0];else cout<<-1;return 0;
}

炮兵阵地


这是一道状压DP的典题!!

题目大意

每一个炮兵部队都有一定的攻击范围,题目要求在地图上最多能安排多少个炮兵部队,而且要满足以下几个条件:
1.各个部队之间不能互相伤害
2.部队不能安排在山坡上

思路梳理

读入

状压DP就是用一串\(0,1\)来表示当前状态,读入时要将每一行的字符转化为二进制,每次读入一个字符,如果为\(H\)那么二进制串的当前位是\(1\),表示山坡,否则是\(0\)表示平地。

for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){char x;cin>>x;a[i]<<=1;if(x=='H')a[i]+=1;}
}

预处理

为了方便统计每种状态下能安排的炮兵部队的个数,即\(1\)的个数,所以需要预处理,每个\(01\)串中\(1\)的个数

int getsum(int S){int tot=0;while(S){if(S&1)++tot;S>>=1;}return tot;
}

动态规划方程

因为一个炮兵部队的攻击范围涉及当前行状态\(s\),上一行状态\(l\),上上行状态\(fl\),当前行数\(i\),显然设一个四位数组\(f[s][l][fl][i]\),空间复杂度为\(O(2^{30}*100)\),显然会炸空间,所以简化设一个三维数组,\(f[s][l][i]\)表示当前状态为\(s\),上一状态为\(l\),当前行数为\(i\)经过反复推敲,动态规划方程

\[f[s][l][i]=max(f[s][l][i],f[l][fl][i-1]+sum[s]) \]

但是发现此时的空间复杂度\(O(2^{20}*100)\),约为\(10^8\),但是见过世面的我们发现动态规划方程只与前三维有关,所以开滚动数组好啦!

条件限制

1.部队只能安排在山坡上,则

\[a[i]\&S!=0 \]

因为如果结果为\(1\),则表示,当前地形为山坡,并且还安排了炮兵部队
2.部队间的左右距离必须大于\(2\)

\[s\&(s<<1)!=0,S\&(s<<2)!=0 \]

如果为\(1\),就表示存在一位\(i\),右边的\(i+1\)\(i+2\),与他都为1,或者左边的\(i-1\)\(i-2\),与他都为1
3.部队间的前后距离必须大于\(2\)

\[s\&fl!=0\&\&s\&l!=0 \]

理解类似于1

\(code\)

注意!!!状态的最大值为\(111...1\)\(2^m-1\)所以代码中要写 $$<2^m$$

#include <bits/stdc++.h>
using namespace std;
int n,m,a[110];
int sum[1<<10],f[1<<10][1<<10][5];
int ans=0;
int getsum(int S){int tot=0;while(S){if(S&1)++tot;S>>=1;}return tot;
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>m;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){char x;cin>>x;a[i]<<=1;if(x=='H')a[i]+=1;}}for(int i=0;i<(1<<m);i++){sum[i]=getsum(i);}for(int S=0;S<(1<<m);S++){if(!(S&a[1]||(S&(S<<1))||(S&(S<<2)))){f[1][S][1]=sum[S];}}for(int l=0;l<(1<<m);l++){for(int s=0;s<(1<<m);s++){if(!(l&s||(l&a[1])||(s&a[2])||(l&(l<<1))||(l&(l<<2))||(s&(s<<1))||(s&(s<<2))))f[l][s][2]=sum[s]+sum[l];}}for(int i=3;i<=n;i++){for(int fl=0;fl<(1<<m);fl++){if((fl&a[i-2])||(fl&(fl<<1))||(fl&(fl<<2)))continue;for(int l=0;l<(1<<m);l++){if(l&a[i-1]||(l&fl)||(l&(l<<1))||(l&(l<<2)))continue;for(int s=0;s<(1<<m);s++){if(s&l||(s&fl)||(s&a[i])||(s&(s<<1))||(s&(s<<2)))continue;f[l][s][i%3]=max(f[l][s][i%3],f[fl][l][(i-1)%3]+sum[s]);}}}}for(int l=0;l<(1<<m);l++){for(int s=0;s<(1<<m);s++)ans=max(ans,f[l][s][n%3]);}cout<<ans;return 0;
} 

吃奶酪

这也是一类状态压缩DP题目

思路

根据题意,必须经过每个节点有且只有一次,显然走过的路程是一条链,而不是一棵树,所以没法用最小生成树做!!
接下来考虑状态,每次吃一块奶酪,对总路程的贡献是与他上一次吃的奶酪所连边的长度,所以显然我们需要一维数组来记录他这一次吃的是哪块奶酪,同时我们还需要记录已经有哪些节点已经在这条链上,也就是哪些奶酪已经被吃过了,于是状态就出来了.\(f[i][j]\)表示当前吃的是第\(i\)块奶酪,已经吃的奶酪为\(j\)(这里用到了状态压缩)。

状态转移方程

转移方程是非常显然的,既然我们已经知道当前吃的是\(i\)块奶酪,吃过的奶酪为\(k\),那么我们就只用枚举上一次吃的\(j\)块奶酪,在加上\(i,j\)之间的边长\(d[i][j]\)就好了.

\[f[i][k]=min(f[i][k],f[j][k^(1<<(i-1))]+d[j][i]) \]

\(code\)

注意!!!! 在转移时因为\(i,j\)并不具有单调性,而\(k\)因为是将第\(i\)位从1变成\(0\),所以具有单调性,因此要将\(k\)放到循环的最外层

 #include<bits/stdc++.h>#define maxn 16using namespace std;struct node{double x,y;}a[maxn];int n;double f[maxn][1<<16],d[maxn][maxn];double calc(int x,int y){double dx=a[x].x-a[y].x;double dy=a[x].y-a[y].y;return sqrt(dx*dx+dy*dy);}int main(){cin>>n;a[0].x=a[0].y=0;for(int i=1;i<=n;i++){cin>>a[i].x>>a[i].y;d[0][i]=calc(0,i);d[i][0]=d[0][i];}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){d[i][j]=calc(i,j);}}memset(f,127,sizeof(f));double ans=f[0][0];for(int i=1;i<=n;i++){f[i][1<<(i-1)]=d[0][i];}for(int i=1;i<(1<<n);i++){for(int j=1;j<=n;j++){if(!(i&(1<<(j-1))))continue;for(int k=1;k<=n;k++){if(k==j)continue;if(!(i&(1<<(k-1))))continue;f[j][i]=min(f[j][i],f[k][i-(1<<(j-1))]+d[k][j]);//cout<<f[j][i]<<endl;}}}// for(int i=1;i<=n;i++){//     for(int j=1;j<(1<<n);j++){//         cerr<<f[i][j]<<endl;//     }// }for(int i=1;i<=n;i++){ans=min(ans,f[i][(1<<n)-1]);}printf("%.2lf",ans);return 0;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/796173.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RustPython简单使用

RustPython介绍 同CPython,Jpython,PyPy一样,RustPython,是使用Rust语言实现的Python解释器,支持Python3语法。 项目地址:https://github.com/RustPython/RustPython RustPython真正方便的是可以编译成Wasm文件,可以直接在浏览器中使用,示例网站:https://rustpython.g…

【解题报告】P8478 「GLR-R3」清明

我无可代替,哪怕来历已不神秘;麦克风接力,百万人就等我出席。P8478 「GLR-R3」清明 参考了出题人题解和 xcyyyyyy 大神的题解,强推前两篇。 拿到题完全没思路怎么办??? 人类智慧的巅峰,思维量的登峰造极。 换句话说就是非人题目,不过不得不说 GLR 的题是真的好,难度也…

Openwrt安装ddns-go

必备条件已刷好OpenWRT的路由 Openwrt已配置好网络根据CPU架构下载DDNS-go 我用的是迅雷赚钱宝1代,其CPU是arm7,所以要下载对应的arm7版本 https://github.com/jeessy2/ddns-go/releases 解压文件,将文件复制到openwrt 用WinSCP连接OpenWRT,复制ddns-go进去 WinSCP下载 如果…

python如何使用 秘钥证书 进行 SM2 加密

最近一个项目,需要使用sm2非对称加密,对方直接给的秘钥证书,python使用gmssl 进行加密,解密,加签,验签用的秘钥是这种格式 # Private Key秘钥 5aa03412c3051e1d4cf9d19cfbeeec70c28f388c9f82747cc912096c9cd44bea # Public Key 公钥 044291b381a039a8d7d02d7272d2d7c78a30d33e…

让小爱音箱播放电脑/NAS上歌曲,支持自动从哔哩哔哩/油管下载歌曲,无需刷机。支持语音控制和WebUI控制,docker部署多平台兼容,解决仅能播放试听版的苦恼

小米AI音箱很多人都有,但使用中播放歌曲时总是提示仅能播放试听版,不能完整听歌,很烦人。今天介绍的方法就是要彻底解决这个问题,实现让小爱AI音箱能够播放本地歌曲,本地没有的歌曲还能自动从网上搜索下载的功能。 已测试支持的设备:型号 名称L06A 小爱音箱L07A Redmi小爱…

Linux系统搭建性能测试监控体系

一.安装Grafana 1.Grafana介绍: Grafana是一个开源的监控和可视化工具,用于显示和跟踪各种指标,数据和日志,支持多种源,包括influxDB、prometheus、mango DB,Redis,Mysql,PostgreSQL等。它提供多种图标类型,饼图,支持设置预警机制,当监控指标超出预定阈值时,可以通过em…

公胶壳和母胶壳

公胶壳和母胶壳 在电子连接器领域,"公胶壳"和"母胶壳"是两种常见的术语,它们通常用来描述连接器的性别,即插头和插座。公胶壳:通常指的是连接器的阳性部分,也就是带有突出的针脚或插销的部分。这些针脚可以插入母胶壳中的相应孔洞,以完成电路的连接。…

2024秋软件工程个人作业(第二次)

这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzu/SE2024这个作业要求在哪里 https://edu.cnblogs.com/campus/fzu/SE2024/homework/13253这个作业的目标 要求使用Python编写一个“羊了个羊”风格的消除类小游戏。对开发过程进行实践,更加熟悉aigc工具学号 102202135…

PbootCMS网站apache伪静态规则

<IfModule mod_rewrite.c>Options +FollowSymlinksRewriteEngine OnRewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !-fRewriteRule ^(.*)$ index.php?p=$1 [QSA,PT,L]</IfModule>扫码添加技术【解决问题】专注中小企业网站建设、网站安全1…

PbootCMS网站nginx伪静态规则

nginx #请复制下面伪静态配置到nginx配置文件中: #规则适合PbootCMS V2.0+版本location / {if (!-e $request_filename){rewrite ^/(.*)$ /index.php?p=$1 last;} }扫码添加技术【解决问题】专注中小企业网站建设、网站安全12年。熟悉各种CMS,精通PHP+MYSQL、HTML5、CSS3、J…

记一次SSH无法远程

故障现象 主机无法使用ssh远程 输入密码之后报错如下# Permission denied (password,keyboard-interactive).原因 看起来好像是远端设置了不允许用户名和密码登录 其实是客户端设置问题 修复或者注释# ChallengeResponseAuthentication no

bluecms搭建和代码审计(SQL)

bluecms搭建 将bluecms的源码文件bluecms放在www目录下,进入bluecms/install/index.php目录安装bluecms填写数据库配置信息和管理员账号信息填写完上述信息后,会自动在bluecms数据库下创建这些文件返回主界面 http://127.0.0.1/bluecms后发现管理员不能从前台登录, 需要从ht…