【洛谷P2622】关灯问题II【BFS】【状压】

题目大意:

题目链接:https://www.luogu.org/problemnew/show/P2622
mm个开关和nn盏灯,第ii个开关要么可以开启第jj盏灯,要么可以关上第jj盏灯,要么不对第jj盏灯起作用。求把nn盏灯全部打开的最少步数。


思路:

这道题很明显可以用BFSBFS做。因为对于每一种情况,我们也就只有mm种转移方法,而求的是最优解。
而最多只有10盏灯,所以可以想到用状压。这样每种情况就被压缩成了00~10231023中的一个数。
那么搜就好了。
注意细节。


代码:

#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;
}
posted @ 2018-10-01 11:09  全OI最菜  阅读(108)  评论(0编辑  收藏  举报