9.解谜游戏

题目描述

Description

小张是一个密室逃脱爱好者,在密室逃脱的游戏中,你需要解开一系列谜题最终拿到出门的密码。现在小张需要打开一个藏有线索的箱子,但箱子上有下图所示的密码锁。

每个点是一个按钮,每个按钮里面有一个小灯。如上图,红色代表灯亮,白色代表灯灭。每当按下按钮,此按钮的灯以及其上下左右四个方向按钮的灯状态会改变(如果原来灯亮则灯灭,如果原来灯灭则灯亮)。如果小张通过按按钮将灯全部熄灭则能可以打开箱子。

现在小张给你一些密码锁的状态,请你告诉他最少按几次按钮能够把灯全部熄灭。

Input

第一行两个整数n,m。

接下来n行,每行一个长度为m的01字符串,0表示灯初始状态灭,1表示灯初始状态亮。

Output

一行一个整数,表示最少按几次按钮可以把灯全部熄灭。

Notes

第一个样例见题目描述,第二个样例按左上和右下两个按钮。

测试用例保证一定有解

思路

遍历第一行的所有情况(使用二进制)。对于剩下行中亮灯的,可以通过按下下一行相同列数的灯灭掉。后判断最后一行是否全为灭。最终在所有结果中找最小的。

小坑:

1、要有原始数组的备份版本,不能再原始数组改动

2、每次循环,需要重置的有:原始数组的备份版、存二进制的数组、存答案的变量

3、注意考虑一开始就全灭的情况(可以当最后一行没有全灭时,ans置为-1,即为无效答案)

4、强烈建议写函数,代码更加简洁清晰

代码

#include<stdio.h>
#include<string.h>
#include<math.h>
int arr[20][20]={0}; 	int arr1[20][20]={0};  
long daan[1000000]={0};
long ans=0;
void huan(int *p)//灭亮变化 
{
	if((*p)==1) *p=0;
	else if((*p)==0) *p=1;
}

int cif(int a,int b)//求二次 
{	int re=1;
	if(b==0) return 1;
	else
	{
		for(int o=b;o>=1;o--)
		{
			re=2*re;
		}
		return re;
	}	
}

void push(int a,int b)//按下的变化 
{
	huan(&arr1[a-1][b]);
	huan(&arr1[a][b]);
	huan(&arr1[a+1][b]);
	huan(&arr1[a][b+1]);
	huan(&arr1[a][b-1]);
}


int main()
{
	int n=0,m=0; char ch; int i=0,j=0;
	scanf("%d %d",&n,&m); ch=getchar();
	for(i=1;i<=n;i++)//输入为数字 
	{
		for(j=1;j<=m;j++)
		{
			ch=getchar();//0灭,1亮 
			arr[i][j]=ch-'0';			
		}ch=getchar();
	}
	
	
	for(i=0;i<=cif(2,m)-1;i++)//遍历第一层情况 
	{ans=0;
		for(int s=0;s<n+2;s++)//重置arr1
		{
			for(int t=0;t<m+2;t++)
			arr1[s][t]=arr[s][t];
		}
		
		int er[19]={0}; int zan=i;
		for(j=0;j<m;j++)//二进制 
		{
			er[j]=zan/cif(2,m-1-j); 
			if(er[j]==1)
			{
				zan=zan-cif(2,m-1-j);
			}
		}
		
		for(j=0;j<m;j++)//变第一行 
		{
			if(er[j]==1)	
			{
				push(1,j+1);ans++;
			}			
		} 
		for(int c=2;c<=n;c++)//换剩下的行
		{
		 	for(int d=1;d<=m;d++)
		 	{
		 		if(arr1[c-1][d]==1)	 
		 		{
		 			push(c,d);ans++;
				 }
			 }
		 }	int flag=0;
		 for(int d=1;d<=m;d++)//判断最后一行 
		 {
		 	if(arr1[n][d]==0)	flag++;
		 }
		 if(flag==m) daan[i]=ans;
		 else daan[i]=-1;
	} 
	ans=10000000;
	for(int mm=0;mm<cif(2,m);mm++)
	{
		if(daan[mm]>=0 && daan[mm]<ans)
		ans=daan[mm];
	}
	printf("%d\n",ans);
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值