拉灯游戏 搜索

你玩过“拉灯”游戏吗?25盏灯排成一个5×5的方形。每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。
  我们用数字“1”表示一盏开着的灯,用数字“0”表示关着的灯。下面这种状态

10111
01101
10111
10000
11011

  在改变了最左上角的灯的状态后将变成:

01111
11101
10111
10000
11011

  再改变它正中间的灯后状态将变成:

01111
11001
11001
10100
11011

  给定一些游戏的初始状态,编写程序判断游戏者是否可能在6步以内使所有的灯都变亮

这也算是一道比较经典的问题了吧;

一种很快的做法是,枚举第一行改变了哪些灯的状态,然后我们就得到了第一行的灯的情况,然后为了满足末状态,第一行的未拉开的灯下面的那个灯一定要改变状态,这样就可以顺推下面的灯的状态,如果灯的状态都可以变成1,记录下步数比较即可;

第一遍的时候想如何暴力优化,想出了双向bfs搜索,可以先预处理下从末状态走三步的所有情况,然后在上面枚举的时候也只用走三步就可以了,25^3,枚举不会超过300,但由于常数比较大,容易被卡,我想出了双向bfs之后,便没有考虑在如何优化,上面的做法是后来知道的;

下面的是代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<string>
 5 #include<cstdlib>
 6 #include<ctime>
 7 #include<vector>
 8 #include<algorithm>
 9 #include<queue>
10 #include<map>
11 using namespace std;
12 #define LL long long
13 const int maxn=20,inf=1000000;
14 #define FILE "1"
15 #define up(i,j,n) for(int i=j;i<=n;i++)
16 #define down(i,n,j) for(int i=n;i>=j;i--)
17 namespace OI{
18     short int b[35000000],c[35000000];
19     int q[3000000],head,tail;//oj允许开这么大的数组我也是醉醉哒
20     int n;
21     inline int h(int x,int y){return x*5+y-5;}
22     int dx[5]={0,0,0,-1,1},dy[5]={0,1,-1,0,0};
23     char ch[6][6];
24     int s;
25     void change(){
26         s=0;
27         down(i,5,1)up(j,0,4){s=s<<1;if(ch[i][j]=='1')s++;}
28     }
29     int ans=10;
30     void work(){
31         //freopen(FILE".in","r",stdin);
32         //freopen(FILE".out","w",stdout);
33         scanf("%d",&n);
34         memset(c,10,sizeof(c));
35         while(n--){
36             ans=10;
37             up(i,1,5)scanf("%s",ch[i]);
38             change();
39             head=0,tail=0;q[++tail]=s;int x,y;c[s]=0;
40             int xx,yy;
41             while(++head<=tail){
42                 x=q[head];
43                 if(b[x]<=3&&c[x]<=3)ans=min(ans,b[x]+c[x]);
44                 if(c[x]==3)continue;
45                 up(i,1,5)up(j,1,5){
46                     y=x;
47                     up(k,0,4){
48                         xx=i+dx[k],yy=j+dy[k];
49                         if(xx<1||yy<1||xx>5||yy>5)continue;
50                         y=y^(1<<(h(xx,yy)-1));
51                     }
52                     if(c[x]+1<c[y]){
53                         c[y]=c[x]+1;
54                         if(c[y]<3)q[++tail]=y;
55                         if(b[y]<=3&&c[y]<=3)ans=min(ans,b[y]+c[y]);
56                         if(c[y]==3)c[y]=90;
57                     }
58                 }
59             }
60             up(i,1,tail)c[q[i]]=90;
61             printf("%d
",ans==10?-1:ans);
62         }
63     }
64     void first(){
65         head=0,tail=0;int x,y;
66         q[++tail]=(1<<25)-1;
67         memset(b,10,sizeof(b));
68         b[(1<<25)-1]=0;
69         int xx,yy;
70         while(++head<=tail){
71             x=q[head];
72             if(b[x]==3)continue;
73             up(i,1,5)up(j,1,5){
74                 y=x;
75                 up(k,0,4){
76                     xx=i+dx[k],yy=j+dy[k];
77                     if(xx<1||yy<1||xx>5||yy>5)continue;
78                     y=y^(1<<(h(xx,yy)-1));
79                 }
80                 if(b[x]+1<b[y]){
81                     b[y]=b[x]+1;
82                     if(b[y]<3)q[++tail]=y;
83                 }
84             }
85         }
86     }
87 }
88 int main(){
89     using namespace OI;
90     first();
91     work();
92     //printf("%d
",clock());
93     return 0;
94 }

View Code

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注