了解这道问题我们要知道----位运算;
(1)如果我想知道一个二进制数sum的(从右往左数)第i位(从0开始数)为多少(以c表示):
c=(sum>>i)&1;
(2)如果我想让一个二进制数sum的(从右往左数)第i位(从0开始数)反转:
sum^=(1<<i);
(异或的特点是:一个位上与1异或会被反转,与0异或会保持不变;)
(3)如果我想让一个二进制数sum的(从右往左数)第i位(从0开始数)根据我的想法,我输入num则其为num:
if (num==1){ sum|=(1<<i);
(或运算的特点:与0相或的数不变;与1相或一直为1)
else if (num==0){ sum &=~(1<<i);
(与运算的特点:与1相与的数不变,与0相与一直为0)
有一个由按钮组成的矩阵,其中每行有6个按钮,共5行。每个按钮的位置上有一盏灯。当按下一个按钮后,该按钮以及周围位置(上边、下边、左边、右边)的灯都会改变一次。即,如果灯原来是点亮的,就会被熄灭;如果灯原来是熄灭的,则会被点亮。在矩阵角上的按钮改变3盏灯的状态;在矩阵边上的按钮改变4盏灯的状态;其他的按钮改变5盏灯的状态。
在上图中,左边矩阵中用X标记的按钮表示被按下,右边的矩阵表示灯状态的改变。对矩阵中的每盏灯设置一个初始状态。请你按按钮,直至每一盏等都熄灭。与一盏灯毗邻的多个按钮被按下时,一个操作会抵消另一次操作的结果。在下图中,第2行第3、5列的按钮都被按下,因此第2行、第4列的灯的状态就不改变。
请你写一个程序,确定需要按下哪些按钮,恰好使得所有的灯都熄灭。根据上面的规则,我们知道1)第2次按下同一个按钮时,将抵消第1次按下时所产生的结果。因此,每个按钮最多只需要按下一次;2)各个按钮被按下的顺序对最终的结果没有影响;3)对第1行中每盏点亮的灯,按下第2行对应的按钮,就可以熄灭第1行的全部灯。如此重复下去,可以熄灭第1、2、3、4行的全部灯。同样,按下第1、2、3、4、5列的按钮,可以熄灭前5列的灯。
输入:
5行组成,每一行包括6个数字(0或1)。相邻两个数字之间用单个空格隔开。0表示灯的初始状态是熄灭的,1表示灯的初始状态是点亮的。
输出:
5行组成,每一行包括6个数字(0或1)。相邻两个数字之间用单个空格隔开。其中的1表示需要把对应的按钮按下,0则表示不需要按对应的按钮。
样例输入:
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
样例输出:
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
枚举的另一个特点:能否发现一个局部,一旦局部确定,后面情况都确定;所以只要枚举局部即可;
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int getBit(char c,int k)
{
return ((c>>(5-k))&1);
}
void chanceBit(char & c,int k)//注意& c,因为这里我也想改变实参;&c是C++的能实现上述的方法;
{
c^=(1<<(5-k));
}
void putBit(char array[])
{
for (int p=0;p<5;p++)
{
for (int q=0;q<6;q++)
{
printf ("%d ",getBit(array[p],q));
}
printf ("\n");
}
}
char prelight[6]={},chancelight[6]={},result[6]={};
int main()
{
int num;
for (int i=0;i<5;i++)
{
for (int j=0;j<6;j++)
{
scanf ("%d",&num);
prelight[i]|=(num<<(5-j));
}
}
int swi;
for (int i=0;i<64;i++)
{
swi=i;
memcpy(chancelight,prelight,sizeof(prelight));
for (int j=0;j<5;j++)
{
result[j]=swi;
for (int k=0;k<6;k++)
{
if (getBit(result[j],k))
{
if (k==0)
{
chanceBit(chancelight[j],k);
chanceBit(chancelight[j],k+1);
}
else if (k>0 && k<5)
{
chanceBit(chancelight[j],k);
chanceBit(chancelight[j],k+1);
chanceBit(chancelight[j],k-1);
}
else if (k==5)
{
chanceBit(chancelight[j],k);
chanceBit(chancelight[j],k-1);
}//以上是用来改变一行的;
if (j<4)
{
chanceBit(chancelight[j+1],k);
}
}
}
swi=chancelight[j];
}
if (chancelight[4]==0)
{
putBit(result);
return 0;
}
}
return 0;
}
那么有没有不用位运算的方法?
由上述可知:一个灯是否开关(除第一行以外)取决于它上面一盏灯是否亮暗;而一盏灯的亮暗取决于1.其左边灯的开关2.其右边灯的开关3.其上面灯的开关4.其自身的开关5.原本灯的亮暗
设是否开关为press[i][j],是否亮暗为light[i][j];
有press[i][j]=(press[i-1][j]+press[i-1][j-1]+press[i-1][j+1]+press[i-2][j]+light[i-1][j])%2;(因为两次灯的开关会抵消)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int light[8][8]={},press[8][8]={},same[8]={};
void putpre()
{
for (int i=1;i<=5;i++)
{
for (int j=1;j<=6;j++)
{
printf ("%d ",press[i][j]);
}
printf ("\n");
}
}
void jin()
{
press[1][1]++;
for (int c=1;c<=6;c++)
{
if (press[1][c]>1)
{
press[1][c]=0;
press[1][c+1]+=1;
}
}
}//不用位运算则对第一行的枚举用二进制加法进行才能从000000~111111;
bool alldown()
{
for (int i=2;i<=5;i++)
{
for (int j=1;j<=6;j++)
{
press[i][j]=(press[i-1][j]+press[i-1][j-1]+press[i-1][j+1]+press[i-2][j]+light[i-1][j])%2;//这里不用管j-1与j+1是因为我数组开的够大,j==0与j==7时,press[i][j]==0;
}
}
for (int j=1;j<=6;j++)
{
light[5][j]=(press[4][j]+press[5][j-1]+press[5][j+1]+press[5][j]+light[5][j])%2;
}
for (int j=1;j<=6;j++)
{
if (light[5][j]!=0)
{
return false;
}
}
return true;
}
int main()
{
for (int i=1;i<=5;i++)
{
for (int j=1;j<=6;j++)
{
scanf ("%d",&light[i][j]);
}
}
for (int j=1;j<=6;j++)
{
same[j]=light[5][j];//十分注意这一步:因为我的代码会改变light[5][j],所以要让其复原;
//
// 逐一判断第五行的灯是否都熄灭
if( puzzle[5][j] != (press[5][j] + press[5][j-1] + press[5][j+1] + press[4][j]) %2 )
return false;
————————————————
版权声明:本文为CSDN博主「qianli_jiang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qianli_jiang/article/details/53420362
像这个大佬的代码完全不用担心;
}
while (!alldown())
{
for (int j=1;j<=6;j++)
{
light[5][j]=same[j];
}
jin();
}
putpre();
return 0;
}