成长轨迹56 【ACM算法之路 百炼poj.grids.cn】【枚举】【2811:熄灯问题】
虽然ac了,但后面对自己的代码有些疑问。。。
题目http://poj.grids.cn/practice/2811
//1、将数组开大一些,使用公式时就可以不用考虑边界
//2、熄灯规则:这一层灯亮,则下一层的对应按键按下
//3、无论开关多少次,将其开次数相加,mod2就可以知道其当前状态
//这样处理,第一行的按键并不会改变
//4、遍历第一层所有按键的情况,因为只有开和关,可以利用二进制+1方式遍历
//模拟二进制,每次加1处理以遍历000000~111111
【wa代码】
1 #include <stdio.h>
2 #include <string.h>
3 int state[6][8],press[6][8];//1、将数组开大一些,使用公式时就可以不用考虑边界
4
5 bool deal()
6 {
7 for(int j=1;j<6;j++)
8 for(int k=1;k<7;k++)//2、熄灯规则:这一层灯亮,则下一层的对应按键按下
9 press[j+1][k]=(state[j][k]+press[j][k]+press[j-1][k]+press[j][k-1]+press[j][k+1])%2;
10 //3、无论开关多少次,将其开次数相加,mod2就可以知道其当前状态
11 //这样处理,第一行的按键并不会改变
12
13 for(int k=1;k<7;k++)
14 if((state[5][k]+press[5][k]+press[4][k]+press[5][k-1]+press[5][k+1])%2)
15 return false;
16
17 return true;
18 }
19
20 int main()
21 {
22 int t;
23 scanf("%d",&t);
24 for(int i=0;i<t;i++)
25 {
26 for(int j=0;j<8;j++)
27 state[0][j]=0;
28 for(int j=0;j<6;j++)
29 {
30 state[j][0]=state[j][7]=0;
31 memset(press[j],0,sizeof(press[j]));
32 }
33
34 for(int j=1;j<6;j++)
35 for(int k=1;k<7;k++)
36 scanf("%d",&state[j][k]);
37
38
39 int c=1;
40 while(press[1][c]<=1)//这里c是这次处理中被修改的最后一位
41 //4、遍历第一层所有按键的情况,因为只有开和关,可以利用二进制+1方式遍历
42 //模拟二进制,每次加1处理以遍历000000~111111
43 {
44 c=1;
45 press[1][c]++;
46 while(press[1][c]>1)
47 {
48 if(c==6)
49 break;
50 press[1][c]=0;
51 c++;
52 press[1][c]++;
53 }
54
55 if(deal())
56 {
57 printf("PUZZLE #%d\n",i+1);
58 for(int j=1;j<6;j++)
59 {
60 printf("%d",press[j][1]);
61 for(int k=2;k<7;k++)
62 printf(" %d",press[j][k]);
63 printf("\n");
64
65 }
66 break;//【这里不跳出还会继续查的。。。】
67 }
68 }
69 }
70 return 0;
71 }
【ac标程】
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //看下标程。。后面还有我刚刚那个的改正版
2 #include<stdio.h>
3 int press[6][8];
4 int puzzle[6][8];
5 bool guess(){
6 int i,j;
7 for(i=2;i<=5;i++){
8 for(j=1;j<=6;j++){
9 press[i][j]=(press[i-1][j]+puzzle[i-1][j]+press[i-1][j-1]+press[i-2][j]+press[i-1][j+1])%2;
10 }
11 }
12 for(j=1;j<=6;j++){
13 if(press[5][j]!=(puzzle[5][j]+press[5][j-1]+press[5][j+1]+press[4][j])%2)
14 return false;
15 }
16 return true;
17 }
18 void process(){
19 int c;
20 for(c=1;c<=6;c++)
21 press[1][c]=0;
22 while(!guess()){
23 press[1][1]++;
24 c=1;
25 while(press[1][c]>1){
26 press[1][c]=0;
27 c++;
28 press[1][c]++;
29 }
30 }
31 }
32 int main(){
33 int t,i,n,j;
34 for(i=0;i<8;i++)
35 press[0][i]=puzzle[0][i]=0;
36 for(i=1;i<6;i++)
37 press[i][0]=puzzle[i][0]=press[i][7]=puzzle[i][7]=0;
38 scanf("%d",&t);
39 for(n=1;n<=t;n++){
40 for(i=1;i<=5;i++)
41 for(j=1;j<=6;j++)
42 scanf("%d",&puzzle[i][j]);
43 process();
44 printf("PUZZLE #%d\n",n);
45 for(i=1;i<=5;i++){
46 for(j=1;j<=6;j++){
47 printf("%d ",press[i][j]);
48 }
49 printf("\n");
50 }
51 }
52 return 0;
53 }
分析附后
【我的ac代码】
1 //发现press[1][c]<=1这个判断条件可能有错
2 #include <stdio.h>
3 #include <string.h>
4 int state[6][8],press[6][8];//1、将数组开大一些,使用公式时就可以不用考虑边界
5 int i;
6 bool deal()
7 {
8 for(int j=1;j<6;j++)
9 for(int k=1;k<7;k++)//2、熄灯规则:这一层灯亮,则下一层的对应按键按下
10 press[j+1][k]=(state[j][k]+press[j][k]+press[j-1][k]+press[j][k-1]+press[j][k+1])%2;
11 //3、无论开关多少次,将其开次数相加,mod2就可以知道其当前状态
12 //这样处理,第一行的按键并不会改变
13
14 for(int k=1;k<7;k++)
15 if((state[5][k]+press[5][k]+press[4][k]+press[5][k-1]+press[5][k+1])%2)
16 return false;
17
18 printf("PUZZLE #%d\n",i+1);
19 for(int j=1;j<6;j++)
20 {
21 printf("%d",press[j][1]);
22 for(int k=2;k<7;k++)
23 printf(" %d",press[j][k]);
24 printf("\n");
25 }
26 return true;
27 }
28
29 int main()
30 {
31 int t;
32 scanf("%d",&t);
33 for(i=0;i<t;i++)
34 {
35 for(int j=0;j<8;j++)
36 state[0][j]=0;
37 for(int j=0;j<6;j++)
38 {
39 state[j][0]=state[j][7]=0;
40 memset(press[j],0,sizeof(press[j]));
41 }
42
43 for(int j=1;j<6;j++)
44 for(int k=1;k<7;k++)
45 scanf("%d",&state[j][k]);
46
47 int c=1;
48 while(!deal())
49 //4、遍历第一层所有按键的情况,因为只有开和关,可以利用二进制+1方式遍历
50 //模拟二进制,每次加1处理以遍历000000~111111
51 {
52 c=1;
53 press[1][c]++;
54 while(press[1][c]>1)
55 {
56 if(c==6)
57 break;
58 press[1][c]=0;
59 c++;
60 press[1][c]++;
61 }
62
63 }
64 }
65 return 0;
66 }
【疑问】
我ac和wa的两个结构到底有什么区别呢?
【wrong】
int c=1;
while(press[1][c]<=1)//这里c是这次处理中被修改的最后一位
//4、遍历第一层所有按键的情况,因为只有开和关,可以利用二进制+1方式遍历
//模拟二进制,每次加1处理以遍历000000~111111
{
c=1;
press[1][c]++;
while(press[1][c]>1)
{
if(c==6)
break;
press[1][c]=0;
c++;
press[1][c]++;
}
if(deal())
{
printf("PUZZLE #%d\n",i+1);
for(int j=1;j<6;j++)
{
printf("%d",press[j][1]);
for(int k=2;k<7;k++)
printf(" %d",press[j][k]);
printf("\n");
}
break;//【这里不跳出还会继续查的。。。】
}
}
【right】
bool deal()
{
……
printf("PUZZLE #%d\n",i+1);
for(int j=1;j<6;j++)
{
printf("%d",press[j][1]);
for(int k=2;k<7;k++)
printf(" %d",press[j][k]);
printf("\n");
}
return true;
}
while(!deal())
//4、遍历第一层所有按键的情况,因为只有开和关,可以利用二进制+1方式遍历
//模拟二进制,每次加1处理以遍历000000~111111
{
c=1;
press[1][c]++;
while(press[1][c]>1)
{
if(c==6)
break;
press[1][c]=0;
c++;
press[1][c]++;
}
}
仔细看可以知道,
1、第一个的if(deal())里边的东西被放进了第二个的deal()的return true之前,这两个写法是等价的
2、第二个的while条件做出了修改,但是第一个的if(deal())里边也有break,所以这两个也等价
两个的区别就是第一个有条件press[1][c]<=1,第二个没有。同时两个都有deal()如果成功就跳出的条件
但是这个c是修改的最后一位啊,它跳出的条件只能是最后一位是2.。。既然这样错了,意思是第一个(带有条件press[1][c]<=1)可能在deal()成功之前跳出(因为成功之后就跳出了,就算那个条件在成功之后不满足那也无所谓。所以,只能是在成功之前跳出导致出错),那么就是说。。press[1][c]在>1时还不应跳出,因为deal()还没有成功,再等一下就成功了。。。但是从000000遍历到111111还不成功是什么情况?!
唔。。。求高人解答了。。。