二进制状态压缩式枚举
对于一个序列,如果其包含的元素仅存在两种状态,那么它可以用二进制状态压缩。具体的操作如下:
操作 | 运算 |
---|---|
取出整数 |
(n>>k)&1 |
取出整数 |
n&((1<<k)-1) |
把整数 |
n xor (1<<k) |
对整数 |
n|(1<<k) |
对整数 |
n&(~(1<<k)) |
选自《算法竞赛进阶指南》
这种方法可以大幅减少程序运行的时间和空间常数。
压缩操作
可以二进制状态压缩的序列,有时候它是执行对象,它也可以是执行操作。对这一类序列的操作只有一种:选择部分元素取反。 (还能有什么其他的吗?)
所以我们可以把对整个对象的操作压缩成一个二进制整数
(opt>>i)&1 == 0
表示要对第
允许的情况下,我们把所有可能的操作压缩出来,得到一个序列
超级枚举!
通常我们枚举一个有较多元素(比如 25 个)的对象使用递归求解。但是二进制状态压缩提供了剪短代码的思路。我们枚举可能的操作压缩后对应的整数。这个可以结合例题来看。
费解的开关
这个题显然符合我们刚才所说的规律:我们处理的元素只有 开/关 两种状态。用一个 32 位整型变量(int)来储存要进行的操作。正如蓝书中所说:
但是怎么实现最快最简单呢?采用上文所说的方法。
- 获取所有压缩过后的操作,这里可以打表实现。代码:
#include<iostream>
using namespace std;
signed main(){
for(int i=1;i<=5;i++)
for(int j=1;j<=5;j++){
bool mat[7][7]={};int ans=0;
mat[i][j]=mat[i-1][j]=mat[i+1][j]
=mat[i][j-1]=mat[i][j+1]=1;
for(int t=0;t<25;t++)
ans+=(mat[t/5+1][t-t/5*5+1]<<t);
cout<<ans<<',';
}
return 0;
}
- 依次枚举操作序列。
for(int now=0,orin;now<32;now++)
这里的 now 指的就是操作序列。
- 检验该操作的合法性即可。
整体代码如下:
#include<bits/stdc++.h>
using namespace std;
int cnt,Q,d[25]={35,71,142,284,536,1121,2274,4548,
9096,17168,35872,72768,145536,291072,549376,
1147904,2328576,4657152,9314304,17580032,3178496,
7405568,14811136,29622272,25690112};
signed main(){
scanf("%d",&Q);
while(Q--){
char s;
int ori=0,mcnt=26;
for(int i=0;i<25;i++)
cin>>s,ori+=!int(s-'0')<<i;
for(int now=0,orin;now<32;now++){
orin=ori,cnt=0;
for(int i=0;i<5;i++)
if((now>>i)&1)orin^=d[i],cnt++;
for(int i=0;i<20;i++)
if((orin>>i)&1)
orin^=d[i+5],cnt++;
if(orin==0&&cnt<mcnt)mcnt=cnt;
} if(mcnt>6)puts("-1");
else printf("%d\n",mcnt);
}
return 0;
}
The Pilots Brothers' refrigerator
代码:
#include<cstdio>
#include<iostream>
using namespace std;
int anscnt=17,ans,tar;
int dir[16]={4383,8751,17487,34959,4593,8946,17652,35064,7953,12066,20292,36744,61713,61986,62532,63624};
signed main(){
char s;
for(int i=0;i<16;i++){
cin>>s;
tar+=(s=='+'?1<<i:0);
}
for(int now=0;now<(1<<16);now++){
int cnt=0,nt=tar;
for(int j=0;j<16;j++)
if((now>>j)&1){
nt^=dir[j];
cnt++;
}
if(nt==0&&cnt<=anscnt){
anscnt=cnt;
ans=now;
}
}
printf("%d\n",anscnt);
for(int i=0;i<16;i++)
if((ans>>i)&1)
printf("%d %d\n",i/4+1,i-i/4*4+1);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律