当前位置: 首页 > news >正文

二进制枚举

一、什么是二进制枚举

在计算机科学中,二进制枚举(Binary Enumeration)是一种通过二进制数的位组合来枚举所有可能状态的算法技巧。它常用于解决组合优化、子集生成、状态压缩等问题。

核心思想

对于一个包含 n 个元素的集合,其所有子集的数量为2^{n}(每个元素可以选择「包含」或「不包含」)。而一个 n 位的二进制数恰好有2^{n}种不同的状态,每一位可以表示集合中对应元素的取舍。因此,可以用二进制数的每一种状态来表示集合的一个子集。

二、如和判断该不该使用二进制枚举?或是直接dfs?

1. 问题规模极小(n ≤ 20)

二进制枚举的时间复杂度是 O(2^n * n),当 n 超过 20 时,2^20 ≈ 1e6(可接受),但 2^30 ≈ 1e9(会超时)。因此:

  • 若 n ≤ 20:二进制枚举可行(如枚举 15 个元素的所有子集)。
  • 若 n > 20:二进制枚举几乎必然超时,优先考虑 DFS(结合剪枝)。
2. 状态是简单的 “二元选择”

二进制枚举的本质是用位表示 “选 / 不选”,仅适合每个元素只有两种状态的问题:

  • 例如:枚举集合的所有子集、0-1 背包问题(物品要么选,要么不选)、判断某元素是否在子集中等。
  • 若状态更复杂(如元素可选择多次、需要记录选择顺序、或状态包含数值信息),二进制枚举无法表示,必须用 DFS。
无需剪枝,必须遍历所有状态

如果问题要求遍历所有可能的状态(如统计所有满足条件的子集数量),且规模较小,二进制枚举比 DFS 更简洁。

  • 例如:“找出所有和为 10 的子集”,若 n=15,二进制枚举直接遍历所有 32768 个子集即可,代码比 DFS 更短。

 三、例题

 洛谷传送门:P10449 费解的开关 - 洛谷

分析:

 首先明确题义改变矩阵中任意一个地方的值,都会使得该点上下左右的点进行相应改变。题目要求给定一个矩阵,判断是否以上述规则在六步之内把整个矩阵改成全是1

先看输入

int n;scanf("%d",&n);while(n--){// 按行输入,把每一行当成一个字符串for(int i=0;i<5;i++){cin>>g[i];}

枚举

此处我们的原则是这一行确定了,就不在考虑范围之内

  1. 假设我们已经处理完第 i-1 行,并且通过第 i 行的操作确保了第 i-1 行的灯全部亮着。

  2. 接下来处理第 i 行时,所有操作都在第 i+1 行进行(通过按下 i+1,j 来修复 i,j 的灯)。

  3. 由于第 i+1 行的操作只会影响第 i 行、i+1 行、i+2 行,不会再改变第 i-1 行的状态,因此第 i-1 行的 “全亮” 状态是稳定的,无需再回头。

int res=2e9+10;//因为直接按第二行的灯不是全局最优,所以从第一行开始考虑//但是因为不知道到底怎么按,所以枚举第一行的所有可能 for(int op=0;op<32;op++){//op既可以当枚举的变量,他的二进制也可以表示第一行的状态//因为+1的步长可以使得枚举完所有可能的第一行 //但是要注意的是 因为第一行的状态早在输入的时候就确定了,所以我们枚举的一直都是操作//注意op的二进制比如(00101)表示按下那两个位置为1的灯 memcpy(backup, g, sizeof g);//备份数组 int step=0;//记录操作次数 // 第一行的按法(在这里1按了,0表示不按)for(int i=0;i<5;i++)if(op>>i&1){  //判断第i位是不是1 step++;turn(0,i);}// 然后通过第一行按完之后的状态,按234行for(int i=0;i<4;i++){//原则就是上一行是0就按下这一行,确定了的行一定不能更改 for(int j=0;j<5;j++ ){if (g[i][j]=='0'){step++;turn(i+1,j);// 如果这个位置是灭的,就按下一行对应的位置}}  }bool dark=false;for(int j=0;j<5;j++){if (g[4][j]=='0'){dark=true;break;}}//最后一行无论如何都改不了因为没有下一行给他按了。所有但凡有黑的,这次一定不行 // 对于32种情况的这一种,如果所有的全亮就记录下步数(事实上只记录了最后一行是否dark)if (!dark) res=min(res,step);memcpy(g,backup, sizeof g);}if(res>6) res=-1;cout<<res<<endl;

关灯函数 

void turn(int x,int y){//对于(x,y)的灯进行开关 for(int i=0;i<5;i++){int a=x+dx[i],b=y+dy[i];if(a<0||a>=5||b<0||b>=5) continue;g[a][b]^=1;}
}

Talk is cheap,show me the code  

#include<bits/stdc++.h>
using namespace std;
const int N=6;
int dx[N]={-1,0,1,0,0},dy[N]={0,1,0,-1,0};
char g[N][N],backup[N][N];void turn(int x,int y){//对于(x,y)的灯进行开关 for(int i=0;i<5;i++){int a=x+dx[i],b=y+dy[i];if(a<0||a>=5||b<0||b>=5) continue;g[a][b]^=1;}
}int main(){int n;scanf("%d",&n);while(n--){// 按行输入,把每一行当成一个字符串for(int i=0;i<5;i++){cin>>g[i];}int res=2e9+10;//因为直接按第二行的灯不是全局最优,所以从第一行开始考虑//但是因为不知道到底怎么按,所以枚举第一行的所有可能 for(int op=0;op<32;op++){//op既可以当枚举的变量,他的二进制也可以表示第一行的状态//因为+1的步长可以使得枚举完所有可能的第一行 //但是要注意的是 因为第一行的状态早在输入的时候就确定了,所以我们枚举的一直都是操作//注意op的二进制比如(00101)表示按下那两个位置为1的灯 memcpy(backup, g, sizeof g);//备份数组 int step=0;//记录操作次数 // 第一行的按法(在这里1按了,0表示不按)for(int i=0;i<5;i++)if(op>>i&1){  //判断第i位是不是1 step++;turn(0,i);}// 然后通过第一行按完之后的状态,按234行for(int i=0;i<4;i++){//原则就是上一行是0就按下这一行,确定了的行一定不能更改 for(int j=0;j<5;j++ ){if (g[i][j]=='0'){step++;turn(i+1,j);// 如果这个位置是灭的,就按下一行对应的位置}}  }bool dark=false;for(int j=0;j<5;j++){if (g[4][j]=='0'){dark=true;break;}}//最后一行无论如何都改不了因为没有下一行给他按了。所有但凡有黑的,这次一定不行 // 对于32种情况的这一种,如果所有的全亮就记录下步数(事实上只记录了最后一行是否dark)if (!dark) res=min(res,step);memcpy(g,backup, sizeof g);}if(res>6) res=-1;cout<<res<<endl;}return 0;
}

 

 

 

http://www.dtcms.com/a/282513.html

相关文章:

  • 基于Canal实现MySQL数据库数据同步
  • 百炼Agent MCP与IoT实战(二):阿里云MQTT Broker配置
  • Unity UGUI 无限循环列表组件
  • 倒立摆系统控制器设计报告
  • PyCharm(入门篇)
  • OpenSearch SQL 查询完整指南
  • Spring Boot 缓存 与 Redis
  • Java-74 深入浅出 RPC Dubbo Admin可视化管理 安装使用 源码编译、Docker启动
  • 【Android】TextView的使用
  • 【Fedora 42】Linux内核升级后,鼠标滚轮失灵,libinput的锅?
  • 颠覆NLP十年范式!OpenCSG中文数据集助推CMU无分词器模型登顶SOTA
  • Jetpack Compose 中 Kotlin 协程的使用
  • 重学SpringMVC一SpringMVC概述、快速开发程序、请求与响应、Restful请求风格介绍
  • 【iOS】源码阅读(六)——方法交换
  • Flutter基础(前端教程①①-底部导航栏)
  • 中医舌诊学习软件,图文视频详解
  • Flutter Web 的发展历程:Dart、Flutter 与 WasmGC
  • 2025华为ODB卷-箱子之字形摆放100分-三语言题解
  • 文字图标设计-色彩魔方:动态变色技术实现场景自适应 大学毕业论文——仙盟创梦IDE
  • 【Unity】Mono相关理论知识学习
  • 深入核心:理解Spring Boot的三大基石:起步依赖、自动配置与内嵌容器
  • Kafka——生产者压缩算法
  • IsaacLab学习记录(一)
  • opencv 值类型 引用类型
  • Hadoop架构演进:从1.0到2.0的深度对比与优化解析
  • ARCGIS PRO DSK 颜色选择控件(ColorPickerControl)的调用
  • Lumerical Charge ------ 运行 PN 结仿真
  • 74、搜索二维矩阵
  • Python+Tkinter制作音频格式转换器
  • PDF 转 Word 支持加密的PDF文件转换 批量转换 编辑排版自由