枚举--遍历搜索空间的例子:熄灯问题
问题描述
有一个由按钮组成的矩阵,其中每行有6 个按钮,共5 行。每个按钮的位置上有一盏灯。
当按下一个按钮后,该按钮以及周围位置(上边、下边、左边、右边)的灯都会改变一次。即,
如果灯原来是点亮的,就会被熄灭;如果灯原来是熄灭的,则会被点亮。在矩阵角上的按钮
改变3 盏灯的状态;在矩阵边上的按钮改变4 盏灯的状态;其他的按钮改变5 盏灯的状态。
在下图8-1 中,左边矩阵中用X 标记的按钮表示被按下,右边的矩阵表示灯状态的改变。
与一盏灯毗邻的多个按钮被按下时,一次操作会抵消另一次操作的结果。在图8-2 中,第2
行第3、5 列的按钮都被按下,因此第2 行、第4 列的灯的状态就不改变。根据上面的规则,
我们知道:
1) 第 2 次按下同一个按钮时,将抵消第1 次按下时所产生的结果。因此,每个按钮最多
只需要按下一次。
2) 各个按钮被按下的顺序对最终的结果没有影响。
3) 对第 1 行中每盏点亮的灯,按下第2 行对应的按钮,就可以熄灭第1 行的全部灯。如
此重复下去,可以熄灭第1、2、3、4 行的全部灯。同样,按下第1、2、3、4、5 列
的按钮,可以熄灭前5 列的灯。
对矩阵中的每盏灯设置一个初始状态。请你写一个程序,确定需要按下哪些按钮,恰好
使得所有的灯都熄灭。
输入数据
第一行是一个正整数 N,表示需要解决的案例数。每个案例由5 行组成,每一行包括6
个数字。这些数字以空格隔开,可以是0 或1。0 表示灯的初始状态是熄灭的,1 表示灯的
初始状态是点亮的。
输出要求
对每个案例,首先输出一行,输出字符串“PUZZLE #m”,其中m 是该案例的序号。接
着按照该案例的输入格式输出5 行,其中的1 表示需要把对应的按钮按下,0 则表示不需要
按对应的按钮。每个数字以一个空格隔开。
输入样例
2
0 1 1 0 1 0
1 0 0 1 1 1
0 0 1 0 0 1
1 0 0 1 0 1
0 1 1 1 0 0
0 0 1 0 1 0
1 0 1 0 1 1
0 0 1 0 1 1
1 0 1 1 0 0
0 1 0 1 0 0
输出样例
PUZZLE #1
1 0 1 0 0 1
1 1 0 1 0 1
0 0 1 0 1 1
1 0 0 1 0 0
0 1 0 0 0 0
PUZZLE #2
1 0 0 1 1 1
1 1 0 0 0 0
0 0 0 1 0 0
1 1 0 1 0 1
1 0 1 1 0 1
我的解题思路:
首先根据题目的提示我们可以知道,每行(列)的亮灭只需它下一行(列)的相应的灯来控制。那么我们就可想想到,第一行靠第二行的控制来全灭,同理,第二、三、四行都可以全灭。但这是还有第五行怎么控制呢?没办法了。。。。就在这卡住了。肿么办?枚举!对了,就是枚举!既然最后一行我们不可以控制,那么我们就可以枚举第一行按灯的情况。按或不按,总共2^6次方种情况,不多,只要把第一行的按灯情况,确定了,那么后面的都好办了。然后就遍历第一行的每种按灯情况,看一下哪一种情况下,刚刚好第五行的灯也全灭,那么这个就是我们想要的情况了。
但是在具体程序的实现上,还有很多的技巧要注意的。首先,它是个5X6的矩阵,但是我却用了6x7的矩阵,这样在按边上(5X6的边)的灯的时候,我们也同样适用是五个灯同时变了,这样用一个函数就能实现,不用考虑两种情况。最后输出只把5X6矩阵输出就好了。对于第一行的情况的遍历,我用的是递归的方式来遍历。因为前段时间刚刚好把递归(recursion)又学了一遍。使用递归来实现遍历,屡试不爽啊!主要注意的是状态的恢复,主要是递归退出时候的状态恢复。其它的就没有什么了。具体的看代码吧。
//遍历搜索空间的例子:熄灯问题 #include <stdio.h> #include <stdlib.h>int puzzl[7][8],press[7][8]; //这有个技巧 int nCase; FILE *fp;void input_data() {int row,col;for (row = 1; row < 6; row++){for (col = 1; col < 7; col++){fscanf(fp,"%d",&puzzl[row][col]);}} }void print_press() {int row,col;for (row = 1; row < 6; row++){for (col = 1; col < 7; col++){printf("%d ",press[row][col]);}printf("\n");} }void do_press(int row,int col) {int j;for (j = -1; j < 2; j++){if (puzzl[row][col+j] == 1){puzzl[row][col+j] =0;}else{puzzl[row][col+j] = 1;}}for (j = -1; j < 2; j++){if (j == 0){continue;}if (puzzl[row+j][col] == 1){puzzl[row+j][col] = 0;}else{puzzl[row+j][col] = 1;}}press[row][col] ^= 1;}void deal_first_line(int col) //col 从1开始 {int i,m,n,nFlag;if (col == 7){//此时,第一行的状态已经定了。for (m = 1; m < 5; m++){for (n = 1; n < 7; n++){if (puzzl[m][n] == 1){do_press(m+1,n);}}}nFlag = 1;for (i = 1; i < 7; i++){if (puzzl[5][i] == 1){nFlag = 0;break;}}if (nFlag){//最后一行全为零printf("PUZZLE #%d\n",nCase);print_press();return;}}else{for (i = 0; i < 2; i++) // two status {if (i == 1){do_press(1,col);}deal_first_line(col + 1);if (i == 1){do_press(1,col); //若状态已改变,恢复状态。因为同一个键按两次就会恢复原始状态 }}} }int main() {int nTime;fp = fopen("in.txt","r");fscanf(fp,"%d",&nTime);nCase = 1;while (nCase <= nTime){memset(press,0,sizeof(press));memset(puzzl,0,sizeof(puzzl));input_data();deal_first_line(1);nCase++;} }2013/4/24 22:01
标准答案中的代码很精简,但是我想不到。搜索的方法不只知道叫什么。。。
void enumate( ) {int c;bool success;for ( c = 1; c < 7; c++)press[1][c] = 0;while( guess() == false ) {press[1][1]++;c = 1;while ( press[1][c] > 1 ) {press[1][c] = 0;c++;press[1][c]++;} }
当然,我的判断的那部分也写得太拙劣了。其实按后的灯光情况我们根本就不必去考虑。在枚举出第一行的press状态后,我们可以这样来判断,技巧很重要啊!
bool guess() {int row,col;for (row = 1; row <= 4; row++ ){for (col = 1; col <= 6; col){press[row+1][col] = (puzzl[row][col] + press[row-1][col] + press[row][col] +press[row][col+1] + press[row][col-1]) % 2;}}for (col = 1; col <= 6){if (puzzl[5][col] != (press[4][col] + press[5][col] + press[5][col-1] + press[5][press+1]) % 2 ){return false;}}return true; }2013/4/25 20:42
转载于:https://www.cnblogs.com/Jason-Damon/archive/2013/04/24/3041251.html
枚举--遍历搜索空间的例子:熄灯问题相关推荐
- 串口编程-枚举遍历串口、获取PC所有串口名称、遍历注册表项、RegEnumValue用法
在网上找了几个关于遍历串口的例子,要么代码不完整,要么就有Bug,如读不了串口号大于10以上的. 经过本人的整理,现分享最终代码,vs2008下编译通过. //此方法同样适用于遍历windows开机 ...
- python中item是什么意思中文-Python中使用item()方法遍历字典的例子
Python字典的遍历方法有好几种,其中一种是for...in,这个我就不说明,在Python了几乎随处都可见for...in.下面说的这种遍历方式是item()方法. item() item()方法 ...
- java enum判断_Java Enum枚举 遍历判断 四种方式(包括 Lambda 表达式过滤)
packagecom.miracle.luna.lambda;importjava.util.Arrays;/*** @Author Miracle Luna * @Date 2019/6/9 23: ...
- 【CCCC】L2-008 最长对称子串 (25分),直接枚举遍历
problem L2-008 最长对称子串 (25分) 对给定的字符串,本题要求你输出最长对称子串的长度.例如,给定Is PAT&TAP symmetric?,最长对称子串为s PAT& ...
- python同步枚举(遍历)两个list_拔剑-浆糊的传说_新浪博客
1.方法1: 采用zip函数 names = ["zhang san","li si", "wang wu"] ages = [18, ...
- php用栈遍历目录和文件,php如何遍历目录,php非递归算法遍历目录的例子
function myscandir($pathname){ foreach( glob($pathname) as $filename ){ if(is_dir($filename)){ mysca ...
- python item方法_Python中使用item()方法遍历字典的例子
Python字典的遍历方法有好几种,其中一种是for...in,这个我就不说明,在Python了几乎随处都可见for...in.下面说的这种遍历方式是item()方法. item() item()方法 ...
- Python中使用item()方法遍历字典的例子
来源:http://www.jb51.net/article/54319.htm Python字典的遍历方法有好几种,其中一种是for...in,这个我就不说明,在Python了几乎随处都可见for. ...
- linux数组递增,shell 输入动态数组并遍历的简单例子
[root@wzlvm shell]# cat shell_array_test.sh #!/bin/bash # # # # Aut wzl # Shell Document ## 判断是否传入参数 ...
最新文章
- java获取vdx文件数据_通过文件名获取文件类型ContentType
- 电脑快捷键横屏变竖屏,电脑显示器竖屏横屏来回切换怎么设置
- 10、MySQL锁等待,死锁,死锁检测
- Java中配置加密组件Bouncy_Castle
- mysql事务和锁InnoDB(转)
- 太极虚拟服务器,太极 中标 云服务器
- win7下装ubuntu14.04双系统
- Intellij idea安装JRebel插件 实现代码的热部署
- xshell 无法定位输入点_linux基础知识个人总结
- android qq毛玻璃,如何快速做出毛玻璃背景?有了这个网格渐变神器,1分钟搞定...
- 【web开发】js实现表单提交
- 【BZOJ4049】【Cerc2014】 Mountainous landscape 【凸包】【线段树】
- between and 用法
- hotmai邮箱服务器在境外吗,hotmail服务器如何设置?设置hotmail邮箱账号方法
- IDEA你可能不知道的小工具
- c语言实验内容.doc答案,C语言程序设计实验内容与答案.doc
- VMware Workstation虚拟机备份及磁盘空间回收
- php 计算百分比,百分比计算器
- python脚本案例-python+adb命令实现自动刷视频脚本案例
- Machine Learning 机器学习