洛谷关灯问题2

26 篇文章 0 订阅
22 篇文章 0 订阅

题目描述

现有n盏灯,以及m个按钮。每个按钮可以同时控制这n盏灯——按下了第i个按钮,对于所有的灯都有一个效果。按下i按钮对于第j盏灯,是下面3中效果之一:如果a[i][j]为1,那么当这盏灯开了的时候,把它关上,否则不管;如果为-1的话,如果这盏灯是关的,那么把它打开,否则也不管;如果是0,无论这灯是否开,都不管。

现在这些灯都是开的,给出所有开关对所有灯的控制效果,求问最少要按几下按钮才能全部关掉。
输入输出格式
输入格式:

前两行两个数,n m

接下来m行,每行n个数,a[i][j]表示第i个开关对第j个灯的效果。

输出格式:

一个整数,表示最少按按钮次数。如果没有任何办法使其全部关闭,输出-1

输入输出样例
输入样例#1: 复制

3
2
1 0 1
-1 1 0

输出样例#1: 复制

2

说明

对于20%数据,输出无解可以得分。

对于20%数据,n<=5

对于20%数据,m<=20

上面的数据点可能会重叠。

对于100%数据 n<=10,m<=100

使用DP 将问题所列的状态用二进制数来表示
1 表示灯开
0 表示灯关
枚举m个开关的状态
以灯全开为起点进行枚举操作
<手打可能有误
异或,与,或,好重要~(UP UP UP

for(int i=1;i<=m;i++){
	ss=(1<<n)-1 ;//表示二进制数的n个1的情况
	for(int j=1;j<=n;j++){
		if(cz[i][j]==1&&(ss&(1<<(j-1)))) ss^=(1<<(j-1));
		//如果此时的第j盏灯为1,操作关;
		if(cz[i][j]==-1&&!(ss&(1<<(j-1))) ss|=(1<<(j-1)));
		//。。。。为-1 操作开 
	} 
}

来源~~ 感谢https://www.luogu.org/problemnew/solution/P2622
ss&(1<< j-1)此运算用以检查当前状态的第j位是否为1
1<< j-1意思是将1左移j-1位,移动后只有第j位上是1
若开关操作为1且当前状态第j位是1
则ss^=(1<< j-1) 表示 1与1异或后得0
若开关操作为-1且当前状态第j位是0
则ss|=(1 << j-1) 表示 1或0 后得1 (此处异或操作也对,因为1异或0得1)
之后便用相同的方法在得到的每个状态上再不断搜索每个状态
由于是BFS,所以一旦某一步状态表示为0(十进制二进制的0写法都是0)
则当前步数必定是最短操作数
若遍历完所有状态都没有0,则输出-1
记得搜索过的状态要打vis标记避免重复访问

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
int n,m,cz[150][20];
int vis[1<<11];
struct node{
	int s,step;
};
int bfs(){
	queue<node> q;
	q.push(node{(1<<n)-1,0}) ;
	vis[(1<<n)-1]=1;
	int i,j;
	while(!q.empty() ){
		node now=q.front() ;
		q.pop() ;
		if(now.s ==0){
			return now.step ;
		}
		int k;
		for(i=1;i<=m;i++){
			k=now.s ;
			for(j=1;j<=n;j++){
				if(cz[i][j]==1&&(k&(1<<(j-1)))) k^=(1<<(j-1));
				else if(cz[i][j]==-1&&!(k&(1<<(j-1)))) k|=(1<<(j-1));
			} 
			if(!vis[k]){
				vis[k]=1;
				q.push(node{k,now.step +1}); 
			}
		}
	}
	return -1;
} 
int main(){
	scanf("%d%d",&n,&m);
	int i,j;
	for(i=1;i<=m;i++){
		for(j=1;j<=n;j++){
			scanf("%d",&cz[i][j]);
		}
	}
	printf("%d",bfs());
	
}

第一次写DP类的BFS ~
加油panda

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值