費解的開關 - 題解

Jerrycyx發表於2024-07-29

費解的開關

時間限制: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;
}

相關文章