2025年熄灯问题详解

熄灯问题详解输入 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

大家好,我是讯享网,很高兴认识大家。


讯享网

 

 

 

 

输入

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

以上就是熄灯问题的内容,接下来我们来分析

一、分析问题

1、简单分析

由各个按钮对灯的影响可知:

a、一个按钮最多只需要按一次

b、按下按钮的顺序对结果不会有影响

2、深入分析

通过按钮的影响以及灯的排列方式,我们可以简单的给出两个办法:

a、

暴力法,枚举,2 的 30 次方 是很大的一个数,如此的话这个程序对时间与空间的使用都是极大的,所以不使用这个方法(因为特别容易超时)

b、

第2个方法是,在第 i 行 的灯可以由  第 i + 1 行的按钮来熄灭 (i<5)

比如,第 1 行的 灯的状态是                                                                                                                                                                      0 1 0 1 1 0   

那么我们第 2 行的按钮就根据第一行的灯的状态来操作,即 第 2 行的按钮的操作为                                                                                0 1 0 1 1 0                                                                                   (1为按下按钮,0不按下按钮)(注意!!!就是说第 i+1 行的操作结果等于 第 i 行的灯的状态!记住且理解这个!待会要考)(如果无法理解接受可以自己在草稿纸上演练一遍,前面有例子)(最好自己在草稿纸上演练一遍加以理解

这样第 1 行的灯就全熄灭了,然后第 2 行的灯接着由第 3 行的按钮来熄灭

但是问题来了,最后一行的灯如何熄灭?

我们只有5行的灯,前面 4 行都由它们的 i+1 行来熄灭,而第 5 行却没有它的 i+1 行帮它熄灭,所以第 5 行,也就是最后一行只能靠自己,也就是说 第 5 行不仅要熄灭第 4 行的灯,同时还要在熄灭第 4 行的灯的同时,也要恰好能熄灭自己第 5 行的灯

为了第 5 行的灯能恰好熄灭,所以我们就要枚举第 1 行的灯在进行了怎样的操作之后,能够在后续 i+1 行的操作下,使第 5 行的灯能恰好熄灭

比如,我第 1 行的操作是 0 0 0 0 0 0 的情况下,后续操作能够使最后一行灯恰好熄灭(即不操作最后一行也能熄灭)

或者如果不行,那么枚举下一种情况,即我第 1 行的操作是 0 0 0 0 0 1 的情况下,后续操作能够使最后一行灯恰好熄灭(即我按了一次第 1 行第 6 列的那妞,然后最后一行恰好熄灭)

如果不行则继续枚举下去

枚举的长度为 2的6次方  64     可以接受,不容易超时

分析完毕,接下来,我们开始设计算法

二、设计算法

1、输入值的保存

对于灯的状态值的保存我们直接就能想到用整型数组来保存,

但是我们再看一下输入样例和输出样例,它们的数值都只涉及到 1、0,就是说在整个运算过程中我们的运算只需要能处理 1、0 即二进制运算即可  

二进制对应的计算也就是位运算,为了方便位运算,所以我们不用整型数组保存,而采用 char 类型数组来保存

因为 char 类型由一个字节8位长度,所以保存一行6个灯的状态的存储空间就足够了

(先记住这个,如何保存我们一会儿再说。因为这个不是算法的核心,然后忘了位运算的话可以百度一下,很容易就想起来的)

2、写下完整代码

#include<iostream> #include<cstring> using namespace std; int getBit(char c,int i) // 取得c的第i比特位 { return (c>>i) & 1; } void setBit(char & c,int i,int v) // 这个函数主要用于保存输入的灯的状态 { if( v ) c |= 1<<i; else c &= ~(1<<i); } void Filp(char & c,int i) // 这个函数用于反转灯的状态 即 0 变 1 ,1 变 0 { c ^= 1<<i; } void OutputResult(char result[],int t) //输出结果 { cout<<"PUZZLE #"<<t<<endl; for(int i=0; i<5; ++i) { for(int j=0; j<6; ++j) { cout<<getBit(result[i],j); if(j<5) cout<<" "; } cout<<endl; } } int main() { char oriLights[5]; // 用于保存灯的原始状态 char lights[5]; // 这个是在枚举操作状态过程中用来变化枚举的灯的状态 char result[5]; // 用来保存结果 char switchs; // 用来枚举第一行的灯的操作 int n; cin>>n; for(int t=1; t<=n; ++t) { memset(oriLights,0,sizeof(oriLights)); // 初始化数组 for(int i=0; i<5; ++i) // 将原始灯的状态保存 { for(int j=0; j<6; ++j) { int s; cin>>s; setBit(oriLights[i],j,s); } } for(int i=0; i<64; ++i) // 用来枚举结果 { switchs = i; // 这是第 1 行的操作 memcpy(lights,oriLights,sizeof(oriLights)); // 不使用strcpy的原因是 str是字符串的拷贝,mem是字节拷贝,无限制数据类型 str总会拷贝‘\0’,有溢出风险 for(int j=0; j<5; j++) // 遍历每一行 { result[j] = switchs; // 将操作结果保存 for(int k=0; k<6; k++) // 遍历每一位 { if( getBit(switchs,k)){ // 返回为 1 则需要操作,即改变所影响到的灯的状态 if(k>0) // 改左边 Filp(lights[j],k-1); Filp(lights[j],k); // 改自己 if(k<5) Filp(lights[j],k+1); // 改右边 } } if(j<4) // 改下一行的收到操作影响的灯的状态 lights[j+1] ^= switchs; switchs = lights[j]; // 考点: i 行的灯的状态等于 i+1 行的操作 } if(lights[4] == 0) 判断最后一行的灯的状态是否是全部熄灭 { OutputResult(result,t); break; } } } // for(int k=0; k<n; ++k) return 0; }

讯享网

大致就是如此了,如有不懂,可评论

小讯
上一篇 2025-01-06 21:15
下一篇 2025-03-24 18:49

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/70433.html