枚举的第5题,咋一看没有思路。仔细研究样列,又找到了该游戏 ,实践了一下,才稍微有些思路。
实际上答案基本在题干中:根据上面的规则,我们知道1)第2次按下同一个按钮时,将抵消第1次按下时所产生的结果。因此,每个按钮最多只需要按下一次;2)各个按钮被按下的顺序对最终的结果没有影响;3)对第1行中每盏点亮的灯,按下第2行对应的按钮,就可以熄灭第1行的全部灯。如此重复下去,可以熄灭第1、2、3、4行的全部灯。
可是第5行的灯如何熄灭呢?
这是本题的关键,谁会影响到最后一行的结果呢?2灭1,3灭2...,只有第一行的操作会影响到最后一行。所以可以枚举第一行的所有情况2^6.
第一行的0~63共64种情况,可以和二进制一一对应,比较朴素的方法是:
for (int k=0;k<=63;k++){//枚举第一行的灯被按下的所有情况(2^6=64)
....
for (int j=1;j<=6;j++){//判断第一行的j灯是否被按下,按下则press[1][j]=1;
int n=pow(2,6-j);//取二进制的第6-j位
pres[1][j]=1&&(k&n);//如果第6-j位 存在,则按下,即值变为1;
if (pres[1][j]==1){
a[1][j]=!a[1][j];a[2][j]=!a[2][j];a[1][j-1]=!a[1][j-1];a[1][j+1]=!a[1][j+1];
}//按下[1][j]后的变化
}
......
而:int n=pow(2,6-j);//取二进制的第6-j位
pres[1][j]=1&&(k&n);//如果第6-j位 存在,则按下,即值变为1;
可以简写为:
pres[1][j]=1&(k>>6-j);//如果第6-j位 存在,则按下,即值变为1;
即k左移6-j位(即k/2^(6-j))后的值与1相与,这样正好取出来第一位二进制数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cmath> #include<cstring> int a[200][200],b[200][200]; int pres[200][200]={0}; int main(){ //freopen("1813.in","r",stdin); //freopen("1813.out","w",stdout); for (int i=1;i<=5;i++) for(int j=1;j<=6;j++) scanf("%d",&a[i][j]); memcpy(&b,&a,sizeof(a)); for (int k=0;k<=63;k++){//枚举第一行的灯被按下的所有情况(2^6=64) memcpy(&a,&b,sizeof(b));//赋初值 memset(pres,0,sizeof(pres));//初始化 for (int j=1;j<=6;j++){//判断第一行的j灯是否被按下,按下则press[1][j]=1; // int n=pow(2,6-j);//取二进制的第6-j位 pres[1][j]=1&(k>>6-j);//如果第6-j位 存在,则按下,即值变为1; if (pres[1][j]==1){ a[1][j]=!a[1][j];a[2][j]=!a[2][j];a[1][j-1]=!a[1][j-1];a[1][j+1]=!a[1][j+1]; }//按下[1][j]后的变化 } for(int i=1;i<=4;i++) for(int j=1;j<=6;j++) if (a[i][j]==1){ a[i+1][j]=!a[i+1][j];a[i][j]=!a[i][j];a[i+1][j-1]=!a[i+1][j-1];a[i+1][j+1]=!a[i+1][j+1];a[i+2][j]=!a[i+2][j]; pres[i+1][j]=1; } int i,j,sum=0; for (int j=1;j<=6;j++) sum=sum+a[5][j]; if (sum==0) { for ( i=1;i<=5;i++){ for ( j=1;j<=5;j++) printf("%d ",pres[i][j]); printf("%d\n",pres[i][6]); } } } return 0; }