奇怪的电视【广度优先搜索】+【位运算】

奇怪的电视

题目描述

小明过年的时候去姥姥家,除夕之夜,大家都想看春节联欢晚会,而可以依赖的就是一台旧电视机

那一台旧电视不是遥控器控制的,上面有许多按钮。按下某一按钮,其他按钮都将被释放,只有被按的按钮工作(如果其他按钮本来就是释放状态,那么它们保持不变,这对下文依旧有用)。可是当小明到来的那一天,上面的许多按钮突然无法正常工作,现在按下某个按钮后,有一些按钮将被释放,而另外的一些按钮将不改变原状态

经过一番惨无人道的折腾,小明知道按下每一个按钮会产生什么样的效果。现在他只需要第3个按钮正常工作

编写程序帮助小明计算,从给定的状态到只有按钮3工作而其他按钮都被释放这个最终状态所需按下的按钮序列的最短长度

输入描述

第一行包含一个整数N,表示电视剧机的按钮数

第二行包含用一个空格隔开的N个二进制数,表示各按钮的初始状态,0表示相应的按钮是被释放的,1表示相应的按钮是被按下的

接下来的N行,表示按下某个按钮时将有哪些按钮被释放。第M+2行由数字K开头,紧跟着K个数字(按升序排列),表示当按下按钮M时被释放的按钮数及按钮号码(按钮号码用数字1~M表示)。每个按钮不能释放其本身,也可能不释放任何按钮

输入数据保证有解

输出描述

输出一行一个数,必须包含从给定的状态到只有按钮3工作而其他按钮都被释放这个最终状态所需按下的按钮序列的最短长度

样例

输入

3
0 1 1
2 2 3
1 3
2 1 2

输出

2

思路

观察题目,有一个显而易见的结论——很复杂
复杂就会导致一件事——数组和参数很多
而由于本题有很多数组:按钮状态数组规则数组访问数组标记数组。。。。。。
而由于本题最多有20个键,就导致在宽搜过程中状态会很多,状态数组等等的会数不胜数
这就会导致一个最终的1问题——炸内存即内存超限
那我们就要优化了:
由于本题的算法是显而易见的宽搜最短路径),就导致上文列举的等等数组都是必须得,所以优化不能优化算法,那就只能优化存储
显而易见,标记数组是无法优化的,访问数组也是很难优化的,而且这两个不用随着按下其他键而进行过大变化,占的内存其实并不多
那我们就将矛头对准另两个内存怪兽:状态数组规则数组
首先观测状态数组,我们可以发现每按下一个键,状态数组都会发生一个很大的改变,这对于时间和空间都是一个重大的占用。而时间首先因为还够其次要优化就要优化算法不好搞,所以我们只解决空间问题
由于数组的每一位都只有两个状态按钮工作按钮释放,即0和1
那读者们可能会想到:那我把它从 i n t int int改成 b o o boo bool不就好了?( ̄▽ ̄)~*
其实不然, i n t int int b o o l bool bool虽然大四倍,但假如你稍微一算,就会发现一个事实:还会炸
那于是我们的最终解法就出来了:
0和1对应的可能不只是bool的数组,更可能是二进制数
所以只要我们将状态数组改成一个一维的数组,在其中用二进制的一串数字将状态对应的数组连起来,要用时再利用位运算进行操作,就节省了一大批空间(喜
同理,依照这一想法将规则数组进行优化即可(~ ̄▽ ̄)~
(假如代码能力不足写不出来,那就去看AC(〃‘▽’〃))

给一些不会位运算的小可爱准备的科普

先说优点:&&省空间

运算符:

位运算运算符

优先级:

位运算优先级

末尾强行赋1:

a=a|1;

末尾强行赋0:

a=a|1-1;

交换:

a=a^b;
b=a^b;
a=a^b;

a* 2 b 2^b 2b:

a<<b

a/ 2 b 2^b 2b:

a>>b;

取末 k k k位:

a&((1<<k)-1)

取右数第 k k k位:

(a>>k-1)&1

右数第 k k k位赋1:

a|(1<<k-1)

右数第 k k k位赋0:

a&~(1<<k-1)

右数第 k k k位求反:

a^1<<k-1

不用感谢我~~~~(〃‘▽’〃)

AC代码

#include<iostream>
#include<cstdio>
#include<queue>

using namespace std;

int n, s, k, t;
int rule[25];

queue<int> q; //队列
bool st[2000005]; //标记数组
int step[2000005]; //访问数组
void bfs(){
	q.push(s);
	st[s] = 1;
	while (!q.empty()) {
		int t = q.front();
		q.pop();
		//只有按钮3被按下
		if (t == 4) {
			printf("%d", step[4]);
			return;
		}
		for (int i = 0; i < n; i++) {
			//右数第i为为0即第i个按钮被释放
			if ((t & (1 << i)) == 0) {
				int tmp = t & rule[i];
				tmp = tmp | (1 << i);
				if (!st[tmp]) {
					st[tmp] = 1;
					q.push(tmp);
					step[tmp] = step[t] + 1;
				}
			}
		}
	}
}

int main() {
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d", &t);
		//将按钮状态合并为一个二进制数
		if (t == 1){
			s = s | (1 << i);
		}
	}
	for (int i = 0; i < n; i++) {
		scanf("%d", &k);
		rule[i] = ~0;
		for (int j = 0; j < k; j++){
			scanf("%d", &t);
			rule[i] = rule[i] & (~(1 << (t - 1)));
		}
	}
	bfs();
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值