华师一附中OI组
标题:
P2040 打开所有的灯
[打印本页]
作者:
admin
时间:
2018-5-17 13:27
标题:
P2040 打开所有的灯
https://www.luogu.org/problemnew/show/P2040
题目背景
pmshz在玩一个益(ruo)智(zhi)的小游戏,目的是打开九盏灯所有的灯,这样的游戏难倒了pmshz。。。
题目描述
这个灯很奇(fan)怪(ren),点一下就会将这个灯和其周围四盏灯的开关状态全部改变。现在你的任务就是就是告诉pmshz要全部打开这些灯。
例如 0 1 1
1 0 0
1 0 1
点一下最中间的灯【2,2】就变成了
0 0 1
0 1 1
1 1 1
再点一下左上角的灯【1,1】就变成了
1 1 1
1 1 1
1 1 1
达成目标。最少需要2步。
输出2即可。
输入输出格式
输入格式:
九个数字,3*3的格式输入,每两个数字中间只有一个空格,表示灯初始的开关状态。(0表示关,1表示开)
输出格式:
1个整数,表示最少打开所有灯所需要的步数。
输入输出样例
输入样例#1:
0 1 1
1 0 0
1 0 1
输出样例#1:
2
说明
这个题水不水,就看你怎么考虑了。。。。
作者:
admin
时间:
2018-6-9 16:41
这个题很值得一做,大家都认真做做!我的程序一向是值得学习的,请看我的
作者:
黄煦喆
时间:
2018-6-9 19:16
#include<iostream>
#include<queue>
using namespace std;
int b[5][5];
struct mp
{
int a[5][5];
int step;
} f[600];
queue<mp>q;
int num;
int tx[5]= {0,+1,-1,+0,+0};
int ty[5]= {0,+0,+0,-1,+1};
int main()
{
int ss=0;
for(int i=1; i<=3; i++)
for(int j=1; j<=3; ++j)
{
cin>>b[i][j];
ss=ss*2+b[i][j];
}
for(int i=1; i<=3; i++)
for(int j=1; j<=3; j++)f[ss].a[i][j]=b[i][j];
f[ss].step=0;
q.push(f[ss]);
while(q.size()&&!f[511].step)
{
int s=0;
mp t=q.front();
q.pop();
for(int i=1; i<=3; i++)
for(int j=1; j<=3; j++)
s=s*2+t.a[i][j];
if(!f[s].step)
{
f[s].step=t.step;
for(int i=1; i<=3; i++)
for(int j=1; j<=3; j++)
{
mp l=t;
int x=i,y=j;
l.step++;
for(int i=0; i<=4; i++)
{
int gx=x+tx[i],gy=y+ty[i];
l.a[gx][gy]=1-l.a[gx][gy];
}
q.push(l);
}
}
}
cout<<f[511].step;
return 0;
}
复制代码
作者:
admin
时间:
2018-6-9 19:18
第一种做法,每个开关都可以按1下,按2下及以上是没有意义的,所以枚举每个开关按与不按,穷举9个开关的状态。
作者:
admin
时间:
2018-6-9 19:19
第二种做法,dfs,枚举000000000-111111111之间的所有数字,判断
作者:
admin
时间:
2018-6-9 19:19
第三种,BFS。每次可以按1号到9号开关,顺序广搜。我这里为了清晰,写的有点麻烦。
#include<iostream>
#include<iomanip>
using namespace std;
const int mn=600; ///最多512下
int x,y;
int s[mn],d[mn];
///step表示到达此状态需要几下
///d表示进队列的先后顺序
int press(int x,int k) ///按动第k个开关 x变成了y
{
int a[10]={0},i=9;
///x化成2进制
while (x>0){a[i]=x%2;x=x/2;i--;}
///按动
if (k==1) {a[1]=!a[1];a[2]=!a[2];a[4]=!a[4];}
if (k==2) {a[1]=!a[1];a[2]=!a[2];a[3]=!a[3];a[5]=!a[5];}
if (k==3) {a[2]=!a[2];a[3]=!a[3];a[6]=!a[6];}
if (k==4) {a[1]=!a[1];a[4]=!a[4];a[5]=!a[5];a[7]=!a[7];}
if (k==5) {a[2]=!a[2];a[4]=!a[4];a[5]=!a[5];a[6]=!a[6];a[8]=!a[8];}
if (k==6) {a[3]=!a[3];a[5]=!a[5];a[6]=!a[6];a[9]=!a[9];}
if (k==7) {a[4]=!a[4];a[7]=!a[7];a[8]=!a[8];}
if (k==8) {a[5]=!a[5];a[7]=!a[7];a[8]=!a[8];a[9]=!a[9];}
if (k==9) {a[6]=!a[6];a[8]=!a[8];a[9]=!a[9];}
///拼成数字
int y=0;
for (i=1;i<=9;i++) y=2*y+a[i];
return y;
}
int head,tail,temp,t,i;
bool bb;
int main()
{
for (i=1;i<=9;i++) {cin>>t; x=2*x+t;}
if (x==511) cout<<0; ///特殊判断
else
{
head=tail=1;
d[1]=x;
s[x]=1;
bb=0;
while (tail>=head && tail<=520 && !bb)
{
temp=d[head];
for (i=1;i<=9;i++)
{
y=press(temp,i);
bb=y==511;
if (s[y]==0)
{s[y]=s[temp]+1;
tail++;d[tail]=y;
}
}
head++;
/*
cout<<"d ";
for (i=1; i<=10; i++) cout<<setw(3)<<d[i];
cout<<endl;
cout<<"s ";
for (i=1; i<=10; i++) cout<<setw(3)<<s[i];
cout<<endl;
*/
}
}
cout<<s[511]-1;
return 0;
}
复制代码
作者:
张笑宇
时间:
2018-6-9 19:34
来一发dfs
#include<iostream>
#include<cmath>
using namespace std;
int a[5][5],i,j,ans=9999;
bool b[5][5];
bool cmp()
{
for (i=1; i<=3; i++)
for (int j=1; j<=3; j++)
if (a[i][j]==0) return false;
return true;
}
void change(int tx,int ty)
{
a[tx-1][ty]=abs(a[tx-1][ty]-1);
a[tx+1][ty]=abs(a[tx+1][ty]-1);
a[tx][ty-1]=abs(a[tx][ty-1]-1);
a[tx][ty+1]=abs(a[tx][ty+1]-1);
a[tx][ty]=abs(a[tx][ty]-1);
}
void dfs(int x)
{
if (cmp()) ans=min(ans,x);
else for (int k=1; k<=3; k++)
for (int v=1; v<=3; v++)
{
if (b[k][v]==0)///没走过
{
b[k][v]=1;
change(k,v);
dfs(x+1);
change(k,v);
b[k][v]=0;
}
}
}
int main()
{
for (i=1; i<=3; i++)
for (j=1; j<=3; j++) cin>>a[i][j];
dfs(0);
cout<<ans;
return 0;
}
复制代码
作者:
张笑宇
时间:
2018-6-9 19:35
请教孙老师:为什么不对???
#include<iostream>
#include<cmath>
using namespace std;
const int mx=5;
const int mm=10001;///a当前
int a[mx][mx],d[mm],n[mm];///n步数
int i,j,k,head,tail;
bool cmp()
{
for (int ii=1; ii<=3; ii++)
for (int jj=1; jj<=3; jj++)
if (a[ii][jj]==0) return false;
return true;
}
void change(int tx,int ty)
{
a[tx-1][ty]=abs(a[tx-1][ty]-1);
a[tx+1][ty]=abs(a[tx+1][ty]-1);
a[tx][ty-1]=abs(a[tx][ty-1]-1);
a[tx][ty+1]=abs(a[tx][ty+1]-1);
a[tx][ty]=abs(a[tx][ty]-1);
}
int main()
{
for (i=1; i<=3; i++)
for (j=1; j<=3; j++) cin>>a[i][j];
head=1,tail=1;
while (head<=tail)
{
int temp=d[head];
n[temp]++;
for (i=1; i<=3; i++)
for (j=1; j<=3; j++)
{
change(i,j);
if (cmp())
{
cout<<n[temp];
return 0;
}
d[tail]=temp;
tail++;
change(i,j);
}
head++;
}
return 0;
}
复制代码
作者:
admin
时间:
2018-6-9 21:07
评点下上面两个同学的程序
HXZ的tx ty有很大的隐患,简易tr tc
ZXY的a[tx-1][ty]=abs(a[tx-1][ty]-1); 简直是笑死人了,显然可以a[tx-1][ty]=1-a[tx-1][ty]或者 a[tx-1][ty]=!a[tx-1][ty]
作者:
YTC
时间:
2018-6-10 16:10
本帖最后由 YTC 于 2018-6-10 16:44 编辑
优化的循环
#include<iostream>
using namespace std;
const int dx[]={0,-1,0,1,+0};
const int dy[]={0,+0,1,0,-1};
int a[4][4];
int b[4][4];
int k[4][4];
bool judge()
{
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
if(!b[i][j]) return false;
return true;
}
int ans,num=0x7fffffff;
int main()
{
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
cin>>a[i][j];
for(k[1][2]=0;k[1][2]<=1;k[1][2]++)
for(k[2][1]=0;k[2][1]<=1;k[2][1]++)
for(k[2][3]=0;k[2][3]<=1;k[2][3]++)
for(k[3][2]=0;k[3][2]<=1;k[3][2]++)
{
k[1][1]=a[1][1]^k[1][2]^k[2][1]^1;
k[1][3]=a[1][3]^k[1][2]^k[2][3]^1;
k[2][2]=a[2][2]^k[1][2]^k[2][1]^k[2][3]^k[3][2]^1;
k[3][1]=a[3][1]^k[2][1]^k[3][2]^1;
k[3][3]=a[3][3]^k[2][3]^k[3][2]^1;
ans=0;
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
b[i][j]=a[i][j];
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
if(k[i][j])
{
ans++;
for(int ii=0;ii<=4;ii++)
b[i+dx[ii]][j+dy[ii]]=1-b[i+dx[ii]][j+dy[ii]];
}
if(judge()) num=min(num,ans);
}
cout<<num;
return 0;
}
复制代码
作者:
YTC
时间:
2018-6-10 16:32
本帖最后由 YTC 于 2018-6-11 10:40 编辑
YTC 发表于 2018-6-10 16:10
9重循环
广搜
<div class="blockcode"><blockquote>#include<iostream>
#include<queue>
using namespace std;
const int dx[]={0,-1,0,1,+0};
const int dy[]={0,+0,1,0,-1};
queue<int> q;
int step[512];
int a[5][5];
int st;
int hsh()
{
int ret=0;
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
ret=ret*2+a[i][j];
return ret;
}
void unzip(int ret)
{
for(int i=3;i>=1;i--)
for(int j=3;j>=1;j--)
{
a[i][j]=ret&1;
ret/=2;
}
}
int main()
{
for(int i=1;i<=9;i++)
{
int t;
cin>>t;
st=st*2+t;
}
q.push(st);
step[st]=0;
while(!q.empty())
{
int x=q.front();
q.pop();
if(x==511) {cout<<step[x];return 0;}
unzip(x);
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
{
for(int t=0;t<=4;t++)
a[i+dx[t]][j+dy[t]]=1-a[i+dx[t]][j+dy[t]];
int ret=hsh();
step[ret]=step[x]+1;
q.push(ret);
for(int t=0;t<=4;t++)
a[i+dx[t]][j+dy[t]]=1-a[i+dx[t]][j+dy[t]];
}
}
return 0;
}
复制代码
作者:
YTC
时间:
2018-6-10 16:53
深搜
#include<iostream>
using namespace std;
const int dx[]={0,-1,0,1,+0};
const int dy[]={0,0,+1,0,-1};
int a[5][5];
bool v[3][3];
bool judge()
{
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
if(!a[i][j]) return false;
return true;
}
int num=0x7fffffff;
int ans;
/*void dfs(int k)
{
cout<<k;
if(k>=9) return ;
if(judge()) {ans=min(ans,k);return;}
if(!judge())
{
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
if(!v[i][j])
{
v[i][j]=1;
a[i][j]=1-a[i][j];
for(int t=0;t<=3;t++)
a[i+dx[t]][j+dy[t]]=1-a[i+dx[t]][j+dy[t]];
dfs(k+1);
a[i][j]=1-a[i][j];
for(int t=0;t<=3;t++)
a[i+dx[t]][j+dy[t]]=1-a[i+dx[t]][j+dy[t]];
v[i][j]=0;
}
}
}*/
void dfs(int i,int j)
{
if(j>3)
{
i++;
j=1;
}
if(i>3)
{
if(judge())num=min(num,ans);
return;
}
else{
ans++;
for(int t=0;t<=4;t++)
a[i+dx[t]][j+dy[t]]=1-a[i+dx[t]][j+dy[t]];
dfs(i,j+1);
for(int t=0;t<=4;t++)
a[i+dx[t]][j+dy[t]]=1-a[i+dx[t]][j+dy[t]];
ans--;
dfs(i,j+1);
}
}
int main()
{
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
cin>>a[i][j];
dfs(1,1);
cout<<num;
return 0;
}
复制代码
作者:
李思矿
时间:
2018-6-10 17:16
孙老师,
【题目总结】P2040打开所有的灯
作者:
admin
时间:
2018-6-10 17:30
为什么要你们把程序贴上来呢?请看我手动摘取的李思旷的程序片段
string act(string a,int pos)
{
string re=a;
re[pos]=(a[pos]='0'?'1':'0');
if(pos%3!=0) re[pos-1]=(a[pos-1]='0')?'1':'0';
if(pos%3!=2) re[pos+1]=(a[pos+1]='0')?'1':'0';
if(pos/3!=0) re[pos-3]=(a[pos-3]='0')?'1':'0';
if(pos/3!=2) re[pos+3]=(a[pos+3]='0')?'1':'0';
return re;
}
复制代码
大家注意到没有 re[pos-1]=(a[pos-1]='0')?'1':'0'; 要改成 re[pos-1]=(a[pos-1]=='0')?'1':'0'; 两个等于号!!!! 而且也没有必要 最好写成 re[pos-1]=(a[pos-1]=='0')+'0';
作者:
YTC
时间:
2018-6-13 13:23
YTC 发表于 2018-6-10 16:10
优化的循环
数组应该开5乘5的
作者:
admin
时间:
2018-6-13 20:46
楼上的各位都做得很好! 这个题目里面可以学到很多的东西!
作者:
张笑宇
时间:
2018-6-16 20:26
终于用bfs过了!!!
#include<iostream>
#include<cmath>
using namespace std;
const int mx=5;
const int mm=10001;///a当前
int a[mx][mx][mm],n[mm],x[mm],y[mm];///n步数
int i,j,k,head,tail;
bool cmp(int t)
{
for (int ii=1; ii<=3; ii++)
for (int jj=1; jj<=3; jj++)
if (a[ii][jj][t]==0) return false;
return true;
}
void change(int tx,int ty,int t)
{
a[tx-1][ty][t]=1-a[tx-1][ty][t];
a[tx+1][ty][t]=1-a[tx+1][ty][t];
a[tx][ty-1][t]=1-a[tx][ty-1][t];
a[tx][ty+1][t]=1-a[tx][ty+1][t];
a[tx][ty][t]=1-a[tx][ty][t];
}
int main()
{
for (i=1; i<=3; i++)
for (j=1; j<=3; j++) cin>>a[i][j][1];
head=1,tail=2,x[1]=1,y[1]=1;
while (head<=tail)
{
if (cmp(head))
{
cout<<n[head];
return 0;
}
int nx=x[head];
int ny=y[head];
change(nx,ny,head);
for (i=1; i<=3; i++)
for (j=1; j<=3; j++)
a[i][j][tail]=a[i][j][head];
n[tail]=n[head]+1;
if (x[head]==3)
x[tail]=1,y[tail]=y[head]+1;
else x[tail]=x[head]+1,y[tail]=y[head];
tail++;
change(nx,ny,head);
for (i=1; i<=3; i++)
for (j=1; j<=3; j++)
a[i][j][tail]=a[i][j][head];
n[tail]=n[head];
if (x[head]==3)
x[tail]=1,y[tail]=y[head]+1;
else x[tail]=x[head]+1,y[tail]=y[head];
tail++;
head++;
}
return 0;
}
复制代码
作者:
张笑宇
时间:
2018-6-16 21:04
新颖!!!
枚举2,4,6,8位的状态!!!
#include<iostream>
using namespace std;
const int mx=4;
const int mm=10;
int a[mx][mx],i,j,k,v;///a地图
int ans=999;
int main()
{
for (i=1; i<=mx-1; i++)
for (j=1; j<=mx-1; j++) cin>>a[i][j];
for (i=0; i<=1; i++)///2是否按
for (j=0; j<=1; j++)///4是否按
for (k=0; k<=1; k++)///6是否按
for (v=0; v<=1; v++)///8是否按
{
int now[mx][mx];
now[1][1]=(i+j+a[1][1])%2;
now[1][3]=(i+k+a[1][3])%2;
now[3][1]=(j+v+a[3][1])%2;
now[3][3]=(k+v+a[3][3])%2;///算出四个角上的
now[2][2]=(i+j+k+v+a[2][2])%2;///中间的
int s1=0,s2=0,s3=0,s4=0,s5=0;
if (now[1][1]!=1) s1=1,now[1][1]=1;///(1,1)按了
if (now[1][3]!=1) s2=1,now[1][3]=1;///(1,3)按了
if (now[3][1]!=1) s3=1,now[3][1]=1;///(3,1)按了
if (now[3][3]!=1) s4=1,now[3][3]=1;///(3,3)按了
if (now[2][2]!=1) s5=1,now[2][2]=1;///(2,2)按了
now[1][2]=(s1+s2+s5+i+a[1][2])%2;
now[2][1]=(s1+s3+s5+j+a[2][1])%2;
now[2][3]=(s2+s4+s5+k+a[2][3])%2;
now[3][2]=(s3+s4+s5+v+a[3][2])%2;///再算十字架上的
bool b=true;
for (int aa=1;aa<=3;aa++)
for (int bb=1;bb<=3;bb++)
if (now[aa][bb]==0) b=false;
if (b)
{
int sum=s1+s2+s3+s4+s5+i+j+k+v;
ans=min(ans,sum);
}
}
cout<<ans;
return 0;
}
复制代码
作者:
吴语林
时间:
2018-6-25 06:25
#include <algorithm>
#include <iostream>
#include <cmath>
#include <cstring>
#include <map>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <cstdio>
#include <cstdlib>
using namespace std;
int a[40];
struct node
{
int er,step,last;
};
int main()
{
queue<node> q;
for(int i=1;i<=9;i++)
scanf("%d",&a[i]);
int z=0;
for(int i=9,j=1;i>=1;i--,j*=2)
z+=(a[i]*j);
node l={z,0,-1};
q.push(l);
node u;
int t[40];
while(!q.empty())
{
u=q.front();q.pop();
if(u.er==511)
{
printf("%d",u.step);
return 0;
}
z=u.er;
for(int i=1,j=256;i<=9;i++,j/=2)
{
t[i]=z/j;
z%=j;
}
for(int i=1;i<=9;i++)
{
if(i==u.last) continue;
t[i]^=1;
if(i-3>=1)
t[i-3]^=1;
if(i+3<=9)
t[i+3]^=1;
if((i-1)%3!=0)
t[i-1]^=1;
if((i+1)%3!=1)
t[i+1]^=1;
z=0;
for(int k=9,j=1;k>=1;k--,j*=2)
z+=(t[k]*j);
node t2={z,u.step+1,i};
q.push(t2);
t[i]^=1;
if(i-3>=1)
t[i-3]^=1;
if(i+3<=9)
t[i+3]^=1;
if((i-1)%3!=0)
t[i-1]^=1;
if((i+1)%3!=1)
t[i+1]^=1;
}
}
return 0;
}
复制代码
作者:
lyc
时间:
2018-7-2 19:34
应该用bfs快些,不过数据水……dfs加个最优性剪纸就过了……数据大应该T到飞起
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
const int oo = 0x3f3f3f3f;
const int N = 5;
using namespace std;
int a[5][5];
int vis[5][5];
int fx[5] = {+0, +1, -1, +0, +0};
int fy[5] = {+0, +0, +0, -1, +1};
int ans = oo;
void change(int x, int y)
{
for(int i=0; i<=4; i++)
{
a[x+fx[i]][y+fy[i]] = 1 - a[x+fx[i]][y+fy[i]];
}
}
bool check()
{
for(int i=1; i<=3; i++)
for(int j=1; j<=3; j++)
if(a[i][j] == 0)
return 0;
return 1;
}
void dfs(int now)
{
if(now > ans) return;
if(now > 9)
return ;
for(int i=1; i<=3; i++)
for(int j=1; j<=3; j++)
{
if(!vis[i][j]){
change(i, j);
vis[i][j] = 1;
if(check()){
ans = min(ans, now);
change(i, j);
vis[i][j] = 0;
return;
}
dfs(now+1);
change(i, j);
vis[i][j] = 0;
}
}
dfs(now+1);
}
int main()
{
for(int i=1; i<=3; i++)
for(int j=1; j<=3; j++)
cin>>a[i][j];
dfs(1);
cout<<ans;
return 0;
}
复制代码
作者:
admin
时间:
2019-10-27 19:14
楼上Y同学,把我的跳转表技术用得很好,要是能把7-11行的那个也用达标写出来就更好了。你想想看。
欢迎光临 华师一附中OI组 (http://hsyit.cn/)
Powered by Discuz! X3.2