1. 程式人生 > >【洛谷P2622】關燈問題II【BFS】【狀壓】

【洛谷P2622】關燈問題II【BFS】【狀壓】

題目大意:

題目連結:https://www.luogu.org/problemnew/show/P2622
m m 個開關和 n n 盞燈,第 i

i 個開關要麼可以開啟第 j j 盞燈,要麼可以關上第 j j 盞燈,要麼不對第 j
j
盞燈起作用。求把 n n 盞燈全部開啟的最少步數。


思路:

這道題很明顯可以用 B F S

BFS 做。因為對於每一種情況,我們也就只有 m m 種轉移方法,而求的是最優解。
而最多隻有10盞燈,所以可以想到用狀壓。這樣每種情況就被壓縮成了 0 0 ~ 1023 1023 中的一個數。
那麼搜就好了。
注意細節。


程式碼:

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

int n,m,a[101][11];
bool p[1024];

int change(int x,int y)  //轉換
{
	for (int i=1;i<=n;i++)
	{
		if (a[y][i]==1) x|=(1<<(n-i)); 
	    //如果這個開關可以開啟這盞燈,那麼就直接或,因為1 or 1=0 or 1=1
		if (a[y][i]==-1&&(x&(1<<(n-i)))) x^=(1<<(n-i));
		//如果這個開關可以關上這盞燈,那麼就要判斷這一位是不是1,如果是1才異或。
	}
	return x;
}

void bfs()
{
	queue<int> dis;  //最小步數
	queue<int> q;  //狀態(已壓縮)
	q.push(0);
	dis.push(0);
	while (q.size())
	{
		int u=q.front();
		int d=dis.front();
		q.pop();
		dis.pop();
		for (int i=1;i<=m;i++)  //美劇每一個開關
		{
			int v=change(u,i);
			if (p[v]) continue;  //判重
			p[v]=1;
			q.push(v);
			dis.push(d+1);
			if (v==(1<<n)-1)  //全部開啟
			{
				printf("%d\n",d+1);
				return;
			}
		}
	}
	printf("-1");
	return;
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
	 for (int j=1;j<=n;j++)
	  scanf("%d",&a[i][j]);
	bfs();
	return 0;
}