费解的开关 - 题解

费解的开关

时间限制:C/C++ 1000MS,其他语言 2000MS
内存限制:C/C++ 256MB,其他语言 512MB

描述

你玩过“拉灯”游戏吗?
\(25\) 盏灯排成一个 \(5×5\) 的方形。
每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。
游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。
我们用数字 \(1\) 表示一盏开着的灯,用数字 \(0\) 表示关着的灯。
下面这种状态
\(10111\)
\(01101\)
\(10111\)
\(10000\)
\(11011\)
在改变了最左上角的灯的状态后将变成:
\(01111\)
\(11101\)
\(10111\)
\(10000\)
\(11011\)
再改变它正中间的灯后状态将变成:
\(01111\)
\(11001\)
\(11001\)
\(10100\)
\(11011\)
给定一些游戏的初始状态,编写程序判断游戏者是否可能在 6 步以内使所有的灯都变亮。

输入描述

第一行输入正整数 \(n\),代表数据中共有 \(n\) 个待解决的游戏初始状态。
以下若干行数据分为 \(n\) 组,每组数据有 \(5\) 行,每行 \(5\) 个字符。
每组数据描述了一个游戏的初始状态。
各组数据间用一个空行分隔。

输出描述

一共输出 \(n\) 行数据,每行有一个小于等于 \(6\) 的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。
对于某一个游戏初始状态,若 \(6\) 步以内无法使所有灯变亮,则输出 \(−1\)

用例输入 1

3
00111
01011
10001
11010
11100

11101
11101
11110
11111
11111

01111
11111
11111
11111
11111

用例输出 1

3
2
-1

提示

\(0<n≤500\)

代码

暴力枚举+剪枝+记忆化+哈希+卡常 做法(\(\Large\textcolor{red}{\textrm{\textbf{暴力碾标算!}}}\)

详见洛谷

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

short bt[35000000];
inline int compress(bool b[10][10])
{
	int zip=0;
	for(int i=1;i<=5;i++)
		for(int j=1;j<=5;j++)
			zip=(zip<<1)+b[i][j];
	return zip;
}
void work(bool now[10][10],int lx,int ly,int step)
{
	int zip=compress(now);
	if(bt[zip]<=step) return;
	bt[zip]=min(bt[zip],(short)step);
	if(step>=6) return;
	for(int i=1;i<=5;i++)
		for(int j=1;j<=5;j++)
		{
			if(i==lx&&j==ly) continue;
			now[i][j]^=1,now[i-1][j]^=1,now[i+1][j]^=1,now[i][j-1]^=1,now[i][j+1]^=1;
			work(now,i,j,step+1);
			now[i][j]^=1,now[i-1][j]^=1,now[i+1][j]^=1,now[i][j-1]^=1,now[i][j+1]^=1;
		}
	return;
}

bool haha[10][10];
char st[10];
int main()
{
	memset(bt,0x3f,sizeof(bt));
	for(int i=1;i<=5;i++)
		for(int j=1;j<=5;j++)
			haha[i][j]=true;
	work(haha,-1,-1,0);
	int n; scanf("%d",&n);
	while(n--)
	{
		for(int i=1;i<=5;i++)
		{
			scanf("%s",st);
			for(int j=1;j<=5;j++)
				haha[i][j]=st[j-1]-'0';
		}
		int zip=compress(haha);
		printf("%d\n",bt[zip]==0x3f3f?-1:bt[zip]);
	}
	return 0;
}

动态规划 做法

#include<cstdio>
#include<algorithm>
using namespace std;

char st[10];
bool a[10][10];
bool f[10][10];

inline void turn(int x,int y)
{
					f[x+1][y]^=1,
	f[x][y-1]^=1,	f[x][y]^=1,		f[x][y+1]^=1,
					f[x-1][y]^=1;
	return;
}

int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		for(int i=1;i<=5;i++)
		{
			scanf("%s",st);
			for(int j=1;j<=5;j++)
				a[i][j]=st[j-1]-'0';
		}
		int ans=0x3f3f3f3f;
		for(int x=0;x<=(1<<6)-1;x++)
		{
			for(int i=1;i<=5;i++)
				for(int j=1;j<=5;j++)
					f[i][j]=a[i][j];
			int cnt=0;
			for(int i=1;i<=5;i++)
				if((x>>i-1)&1)
				{
					turn(1,i);
					cnt++;
				}
			int step=0;
			for(int i=1;i<5;i++)
				for(int j=1;j<=5;j++)
					if(!f[i][j])
					{
						turn(i+1,j);
						cnt++;
					}
			bool flag=true;
			for(int i=1;i<=5;i++)
			{
				for(int j=1;j<=5;j++)
					if(!f[i][j])
					{
						flag=false;
						break;
					}
				if(!flag) break;
			}
			if(flag) ans=min(ans,cnt);
		}
		printf("%d\n",ans>6?-1:ans);
	}
	return 0;
}
posted @ 2024-07-29 23:55  Jerrycyx  阅读(10)  评论(0编辑  收藏  举报