关灯游戏的简介就不介绍了,还不了解的朋友可点击笔者的上一篇博文----关灯游戏 Lights out (一)(极速求解)

在这一篇给大家介绍“关灯游戏”的枚举算法。

枚举算法很简单,就是枚举所有开关操作,然后逐一检索经过各种操作后局面的最终状态,当灯全灭时输出解。

对一个m×n的灯阵,并不需要枚举整个局面2^(m×n)种开关组合。因为任意一种操作,首行的操作就决定了后面所有行的操作都是唯一确定的,不存在其它可能。

原因很简单,因为能影响第一行灯的亮/灭状态,只可能是第二行的操作组合,并且能让第一行灯全灭的操作是唯一的;接下来,能影响第二行灯的亮/灭状态,只可能是第三行的操作组合,并且能让第二行灯全灭的操作是唯一的...依此类推。所以,当第一行的开关操作确定了,为了使得每一行的灯全灭,后面所有行的开关操作也都确定了。

请看下面图示例子:

一个5×5的灯阵,假设一开始全部灯全亮,如上图所示。

假如某一种开关操作,它的第一行操作如上图所示(1代表对该灯开关操作一次,下同)。

第一行开关操作完毕,局面灯的亮/灭状态如上图所示。

此时我们看到,第一行两端盏两盏灯灭了,中间三盏灯还是亮的。为了使第一行灯全灭,接下来对第二行必须对中间三盏灯开关操作一次,并且不可以操作第二行两端两盏灯,第二行的操作是唯一确定的。(对以后所有行的操作依此类推)

操作完第二行后,局面灯的亮灭状态如上图所示。

操作完第三行后,局面灯的亮灭状态如上图所示。

操作完第四行后,局面灯的亮灭状态如上图所示。

操作完第五行后,局面灯的亮灭状态如上图所示。

当所有行都操作完毕后,检查一下局面,如果局面的灯全灭,则输出该解。如果局面还有灯亮,说明从一开始,第一行的操作就是错的。

上面图示例子,到最后局面的灯全灭了,表明我们找到了解法,就是最后一个图标出的操作组合。

通过上面分析,枚举时,只需要枚举第一行所有的操作组合,对一个m行n列灯阵,枚举量为2^n。时间复杂度是O(2^n),猛一看上去似乎这种算法效率太差。其实不然,因为开/关操作和灯的亮/灭可以对应计算机的二进制数据,这样一来,整个过程可以用位运算来完成,速度极快,求解20×20的灯阵是毫秒内的事情,应付游戏娱乐足够了。

枚举算法非常简单,易于编程实现,求解小关卡速度也很快,所以枚举算法是个不错的选择,没有必要动不动就用线性代数算法求解。

当第一行的开/关操作确定下来后,后面所有行的操作,都必须确保前一行的灯全灭,那么最后检查局面灯是否全灭时,只需要检查最后一行灯是否全灭即可。

所有行开关操作完毕时,如果局面灯全灭,输出该解。

枚举完第一行2^n种不同操作,如果每种操作,最后都无法使得灯全灭,则说明初始局面无解。

灯亮用1表示,灯灭用0表示;对任意一盏灯,有开关操作用1表示,没有操作用0表示(开关操作偶数次等价于0次,开关操作奇数次等价于1次)。

m行n列灯阵,可以用数组Lights[m]来表示灯的亮/灭状态,数组中的每一个数据表示一整行灯的亮/灭状态;用数组press[m]来表示开关操作,数组中的每一个数据表示一整行的开关操作情况。为了读入初始化局面,再增加一个initializtion[m]数组记录初始化灯阵的亮/灭状态。以上三个数组的数据,二进制长度都是n位。

编程很简单,核心代码不过十行左右。下面是首行枚举+位运算完整C++代码( 程序默认初始状态的灯全亮,搜索目标状态为灯全灭;可以读入初始化局面,自动判断有没有解,如果有解,将输出所有解):

#include<iostream>
using namespace std;
long Row,Column,guide,statistic=0;void specification()
{int i;cout<<"\n                       关灯游戏(Lights out)解题程序\n\n";cout<<"    本程序用于求解智力难题----“关灯游戏”,可以求解任意初始局面的灯阵,默认初始";cout<<"状态灯全亮,求解的目标状态是灯全灭。\n";cout<<"    解题开始前要设置行数和列数。行数不限,但列数受数值位数限制,不可以大于30。如";cout<<"果列数不等于行数,那么最好设置行数大于列数,因为本程序行数没有限制,并且,求解一";cout<<"个30行2列的局面,远远快于2行30列的局面。\n";cout<<"    程序允许选择手动输入自定义初始布局。\n";cout<<"    程序会自动判断局面有无解,如果有解,将给出所有解!\n\n";for(i=0; i<80; i++)cout<<"=";
}int choose(long *initializtion)
{long i,j,opening,check=1;char C;cout<<"请选择,是否输入自定义初始局面?  (Y/N):";cin>>C;if((C=='Y')||(C=='y')){cout<<"\n请输入初始局面,1代表亮灯,0代表灭灯。灯之间用空格隔开,输完一行后用回车换行。\n";cout<<"例如,输入一个2行3列,并且有两盏亮灯的初始局面为:\n0 0 1\n0 1 0\n";for(i=0; i<80; i++)cout<<"-";cout<<"\n";for(i=0; i<Row+2; i++)initializtion[i]=0;          //清空默认初始化局面for(check=0,i=1; i<=Row; i++)                      //读入用户设置初始局面{for(j=0; j<Column; j++){cin>>opening;initializtion[i]<<=1;initializtion[i]+=opening;}check|=initializtion[i];}if(check==0)                                       //检查用户输入的局面,如果灯已经全灭,则程序结束cout<<"\n灯已经全灭。\n";}return check;
}void solve(long *lights,long *press,long *initializtion)
{long i,j,L,R,permutation,temp;for(permutation=0; permutation<guide; permutation++)       //从0~2^Column-1逐一枚举第一行操作{lights[0]=permutation;                             //第一行操作等于第0行灯的状态for(i=1; i<=Row; i++)                              //清空后面所有行操作,并重新初始化灯局面{press[i]=0;lights[i]=initializtion[i];}for(i=1; i<=Row; i++)                              //开关灯操作{press[i]=lights[i-1];lights[i+1]^=press[i];                     //第i+1行灯的开关操作,等于第i-1行灯的状态lights[i]^=press[i];L=press[i]<<1;if(guide<=L)L^=guide;lights[i]^=L;R=press[i]>>1;lights[i]^=R;}if(lights[Row]==0)                                  //检查局面,如果灯全灭,则输出解{cout<<"\n\n";statistic++;                                //解数量计数器for(i=1; i<=Row; i++)                       //输出解{temp=guide>>1;for(j=0; j<Column; j++){(press[i]&temp)?cout<<"1 ":cout<<"0 ";temp>>=1;}cout<<endl;}}}
}int main(void)
{/*关灯游戏C++代码位运算版,支持用户输入初始局面;可判断局面有无解;有解局面将给出所有解*/long i;specification();cout<<"\n请输入行数:";cin>>Row;cout<<"请输入列数:";cin>>Column;long *lights=new long [Row+2];           //lights[]用于保存灯的局面状态long *press=new long [Row+2];            //press[]用于保存按灯位置long *initializtion=new long[Row+2];     //initializtion[]用于保存用户输入初始化局面if(Column>30||Row*Column==0)             //这里限制用户输入列不可以大于30,或者行数和列数不可以等于0{if(Column>30)cout<<"\n列数不可超过30\n";goto end;}guide=1<<Column;for(i=0; i<Row+2; i++)lights[i]=initializtion[i]=guide-1; //默认初始化局面为灯全亮if(choose(initializtion)==0)goto end;cout<<"\n解题开始,请稍候------------\n";solve(lights,press,initializtion);statistic?cout<<"\n\n全部解个数:"<<statistic:cout<<"\n此题无解!";
end:cout<<"\n\n运行结束,请按任意键退出程序。";delete []lights;delete []press;delete []initializtion;cin.ignore(),cin.ignore();return 0;
}

上面程序,因为输入输出以及各种判断比较繁琐,导致代码量巨大。笔者曾打算为程序写一个图形界面,但考虑到这样使用起来也不是很复杂,而写GUI会很麻烦,于是作罢。

下面这是笔者用上面程序得到的统计表(独立解和最少操作次数的统计,需要对程序进行改动)

关灯游戏 Lights out (二)(首行枚举+位运算,搜索全部解)相关推荐

  1. 关灯游戏 Lights out (三)(线性代数+高斯消元,搜索全部解)

    关灯游戏和线性代数联系紧密,对于一个 的灯阵,用线性方程组+高斯消元法求解,时间复杂度为O(m×n)^3.相对于首行枚举算法复杂度O(2^n) ,线代算法的时间复杂度低很多.用线性代数求解关灯游戏是个 ...

  2. 关灯游戏c语言,关灯游戏 Lights out (一)(极速求解)

    关灯游戏(Lights Out) 关灯游戏是Tiger Electronics在1995年发行的一款电子游戏,Parker Brothers在上世纪70年代发布过一款规则与此类似的3×3的游戏, Vu ...

  3. c语言 枚举,位运算

    c语言 枚举,位运算 枚举 枚举类型的定义和枚举变量的说明 一.枚举的定义枚举类型定义的一般形式为 二.枚举变量的说明 枚举类型变量的赋值和使用 位运算 位域 一.位域的定义和位域变量的说明 二.位域 ...

  4. 蓝桥备赛第一周2021.1.11 递归 枚举 位运算

    文章目录 递归实现指数型枚举 递归指数型枚举 方法1:肯定是2^n行,所以直接就是上一个动态m从0到n加一堆空行 方法2:以最新的值为n为结束,遇到为0的不输出,用完要恢复为0 递归实现排列型枚举 非 ...

  5. vijos1197-费解的开关【递推,枚举,位运算】

    正题 题目链接:https://vijos.org/p/1197 大意 有5*5个开关,每次选择一个地方时它和它上下左右的开关都会取反,求将所有开关都变成1的最少次数. 解题思路 首先我们知道一个位置 ...

  6. word整个表格首行缩进_教师计算机技术水平考核word题集(二)

    水平考核的客观题请点击上方蓝字关注公众号后,在菜单"师生培训"中的"教师培训"菜单中查看! 31.word文档字号(八号最小,初号最大依次递增,5号最小,72号 ...

  7. 【Visual C++】游戏开发笔记二十七 Direct3D 11入门级知识介绍

    游戏开发笔记二十七 Direct3D 11入门级知识介绍 作者:毛星云    邮箱: happylifemxy@163.com    期待着与志同道合的朋友们相互交流 上一节里我们介绍了在迈入Dire ...

  8. axure中怎么做出固定首行_Excel:固定表头的方法

    Hi,大家好,我们今天讲一下常用的固定表头或者首列的方法,方便翻页时看到首行首列等. 一.功能 将用户需要的行或列进行固定,方便翻页观看. 二.表示 使用视图菜单里的冻结窗格按钮. 三.简单应用 场景 ...

  9. word2007-2010排版中解决段后插入分页符 新页首行空行问题

    word2007-2010排版中,很多人都会遇到 这个问题.当你在 段后插入分页符 想开启新的一页的时候,新页首行有个空行.如果删除,会连同分页符一起删除.不删除有影响排版美观.那怎么解决呢: 解决办 ...

  10. axure中怎么做出固定首行_办公软件操作技巧078:如何在excel表格中冻结行与列...

    在日常工作中,有时我们编辑的excel表格会比较大,数据内容有很多行和列,当我们拖动滚动条找到了离表头比较远的数据行或列的内容时,又看不到行或列表头标题信息了,这时再去处理数据信息就会感觉很不方便,如 ...

最新文章

  1. 使用dom4j解析XML例子
  2. Perseus-BERT——业内性能极致优化的BERT训练方案
  3. Electron中实现拖拽文件进div中通过File对象获取文件的路径和内容
  4. 无监督学习 k-means_无监督学习-第3部分
  5. 计算机网络 ospf重点,计算机网络:OSPF协议概述
  6. JSON基础 JS操作JSON总结
  7. oracle link binaries,Oracle 单实例 Relink Binary Options 说明
  8. 查看WiFi记录日志
  9. 计算机桌面任务栏过宽怎么处理,任务栏变宽了怎么办 还原变宽任务栏的方法【图文教程】...
  10. 流程图-时序图-架构图,部署图-拓扑图-ER图-类图-状态图-用例图
  11. 记 · 迎接寒风 · 独自北上旅行
  12. 最新小程序反编译详细教程,亲测可用
  13. 热更新你都知道哪些?
  14. 微博爬虫——爬取指定范围内所有帖子包含的定位信息的方法
  15. IEEE754的理解归纳
  16. 限定当天的可用次数 redis
  17. firefox v46如何添加xpath checker组件
  18. 肉鸡进程linux,教菜鸟如何获得大量Linux肉鸡网站安全 -电脑资料
  19. [转]mysql的sql优化工具
  20. arch linux格式化,用Arch linux打造自己的操作系统(一)

热门文章

  1. roberts算子实现
  2. EXCEL 导入MSSqlserver数据库报错
  3. Pix4D航测成图完整教程(空三,点云编辑DSM,正射,质量报告分析...)
  4. 常用数据下载网站汇总
  5. STM32工程更换芯片方法
  6. 免费采集软件智能万能采集数据
  7. 联想MIIX520安装黑苹果参考教程
  8. 机械制图与计算机辅助绘图题,机械制图与计算机绘图作业题解ppt课件.ppt
  9. kali攻击wifi、破解wifi密码详细教程(一)
  10. 计算机职业素养论文1500字,【如何提高职业素养1500字】_个人职业素养提升计划1500字范文...