openjudge 1813 熄灯问题
描述
有一个由按钮组成的矩阵,其中每行有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
来源
1222
题解
枚举第一行熄灯的情况
如果上一行是1,则下一行是1
例:100101 下一行也是 100101 就可以关上一行的灯
这样推出后面几行
最后对最后一行进行判断
如果最后一行都是0就找到了答案 下面是代码
使用dfs搜索第一行的情况
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
bool a[10][10];//a记录开关灯情况
bool k[10][10];//原有的开关灯情况
bool c[10][10];//答案数组
void turn(int x,int y){//开关灯的函数
a[x][y]=!a[x][y];
if(x>=2) a[x-1][y]=!a[x-1][y];
if(x<=4) a[x+1][y]=!a[x+1][y];
if(y>=2) a[x][y-1]=!a[x][y-1];
if(y<=5) a[x][y+1]=!a[x][y+1];
}
bool pd(){//判断是否成功关完
for(int i=1;i<=6;i++)
if(a[5][i]==1)
return false;
return true;
}
void deal(){//开关灯
for(int i=1;i<=6;i++){
if(c[1][i]==1)
turn(1,i);
}
for(int i=2;i<=5;i++)
{
for(int j=1;j<=6;j++)
if(a[i-1][j]==1)
{
c[i][j]=1;
turn(i,j);
}
}
}
void makea(){//用a记下k
for(int i=1;i<=5;i++)
for(int j=1;j<=6;j++)
a[i][j]=k[i][j];
}
void clean(){//如果失败 将c清零
for(int i=2;i<=5;i++)
for(int j=1;j<=6;j++){
c[i][j]=0;
}
}
bool flag=0;//判断是否成功
void dfs(int step){//搜索出c[1]的情况
if(!flag){//成功就退出
if(step==7){//找出一种c[1]就进行判断
clean();//清空c
makea();//用a记下k
deal();//开关灯
if(pd()){
flag=1;
}
return;
}
for(int i=0;i<=1;i++)
{
c[1][step]=i;
dfs(step+1);
if(flag==1) return ;//成功就退出
}
}
}
int main()
{
for(int i=1;i<=5;i++)
for(int j=1;j<=6;j++)
scanf("%d",&k[i][j]);
dfs(1);//从1开始
for(int i=1;i<=5;i++){//输出
for(int j=1;j<=6;j++)
cout<<c[i][j]<<" ";
cout<<endl;
}
return 0;
}
update 2017 .6 .29
回档第一篇博客 又是一种心情
觉得那个时候好辣鸡的啊
连个dfs都想半天
学了高斯消元以后 重新来做此题 又花了好长时间(我是制杖吗?。。)
嗯 高斯消元的做法
首先:
1.这个点按奇数次和按一次是一样的 按偶数次和不按是一样的 因为按两次就又回去了
2.按的顺序和答案没有关系
列方程:
一个点最多被五个点影响(上下左右+自己) 因为每个点的次数只是0或者1 所以首先得到方程
像这个样子
(x1+x2+x3+x4+x5)
(mp[i][j]指i,j是否要改变)
但是mod2不是很好处理了
又想到 1和 1 变0 0和1 变1 那么这个时候用异或运算就好
有
x1^x2^x3^x4^x5=mp[i][j]
那么 此题是5*6的格子 需要30个未知数 列30个方程 解一下就行
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cmath>
using namespace std;
int n=5,m=6;int cnt=30;
int mp[50][50];
int a[50][50];
int dx[10]={0,1,-1,0,0};
int dy[10]={0,0,0,1,-1};
void gauss(){
for(int i=1;i<=30;i++){
int k=i;
for(;k<=30;k++)
if(a[k][i]!=0) break;
swap(a[i],a[k]);
for(int p=1;p<=30;p++){
if(p!=i&&a[p][i])
{
for(int j=i;j<=31;j++)
a[p][j]=a[p][j]^a[i][j];//异或方程
}
}
}
}
int mk(int x,int y){
return (x-1)*6+y;
}
bool in_Map(int x,int y){
if(x<1||x>n||y<1||y>m) return false;
return true;
}
void build(){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int plc=mk(i,j);
a[plc][31]=mp[i][j];//要得到的答案即要改变此点的次数
a[plc][plc]=1;
for(int k=1;k<=4;k++){
int tx=i+dx[k];
int ty=j+dy[k];
if(!in_Map(tx,ty)) continue;
a[plc][mk(tx,ty)]=1;
}
}
}
int main(){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&mp[i][j]);
build();
gauss();
int cnt=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
printf("%d ",a[++cnt][31]);
printf("\n");
}
return 0;
}
附:poj1222=此题
代码如下咯:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cmath>
using namespace std;
int n=5,m=6;int cnt=30;
int mp[50][50];
int a[50][50];
int dx[10]={0,1,-1,0,0};
int dy[10]={0,0,0,1,-1};
void gauss(){
for(int i=1;i<=30;i++){
int k=i;
for(;k<=30;k++)
if(a[k][i]!=0) break;
swap(a[i],a[k]);
for(int p=1;p<=30;p++){
if(p!=i&&a[p][i])
{
for(int j=i;j<=31;j++)
a[p][j]=a[p][j]^a[i][j];//异或方程
}
}
}
}
int mk(int x,int y){
return (x-1)*6+y;
}
bool in_Map(int x,int y){
if(x<1||x>n||y<1||y>m) return false;
return true;
}
void build(){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int plc=mk(i,j);
a[plc][31]=mp[i][j];//要得到的答案即要改变此点的次数
a[plc][plc]=1;
for(int k=1;k<=4;k++){
int tx=i+dx[k];
int ty=j+dy[k];
if(!in_Map(tx,ty)) continue;
a[plc][mk(tx,ty)]=1;
}
}
}
int main(){
int T;
scanf("%d",&T);
int nt=0;
while(T--){
printf("PUZZLE #%d\n",++nt);
memset(a,0,sizeof(a));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&mp[i][j]);
build();
gauss();
int cnt=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
printf("%d ",a[++cnt][31]);
printf("\n");
}
}
return 0;
}