关灯问题II(Luogu P2622)
先挂个链接 - https://www.luogu.com.cn/prob...
题面 - 现有N盏灯,M个按钮。每个按钮可以同时控制这n盏灯——按下某个按钮,对于所有的灯都有一个效果。给出所有开关对所有灯的控制效果,问最少要按几下按钮能将灯从全开变为全关
Now,进入正题
瞧下数据,N<=10,M<=100
数据规模不很大,果断BFS(广度优先搜索)
然而......
BFS的判重又成了大问题
逐个比对,看状态是否一致,显然超时
不判重,BFS就直接废了
这时候,新Get到的技能——状态压缩闪亮登场
状态压缩即将原来状态的数组存放变为一个01构成的整数存放
这样就这可以设置一个下标代表状态的vis[]数组来判重
判重瞬间降到O(1)级别
状态的改变则用位运算,也降到O(1)级别
就是这么容易......
下面挂上程序(C++)和精心设计的注解方便理解
//Luogu P2622 - 关灯问题II
//https://www.luogu.com.cn/problem/P2622
//BFS+状压
#include <bits/stdc++.h>
#define MAXN 2048
using namespace std;
int N, M;
struct Node{
int dp; //状压值,反应灯状态
int step; //操作次数
};
int vis[MAXN]; //用于BFS的判重
int a[MAXN][MAXN]; //灯的控制效果
void BFS(int n){
queue<Node> Q;
Node fir;
fir.step = 0, fir.dp = n;
Q.push(fir);
while(!Q.empty()){
Node u=Q.front();
Q.pop();
int pre=u.dp;
//拿出队首
for(int i=1; i<=M; i++){
int now=pre; //now即为状压值,灯的状态
for(int j=1; j<=N; j++){
if(a[i][j]==1){
if((1<<(j-1))&now){
now = now^(1<<(j - 1));
}
}
else if(a[i][j]==-1){
now = ( (1<<(j-1))|now);
}
}
//使用位运算改变状压值,反应了开关对于灯状态的影响
fir.dp=now, fir.step=u.step+ 1;
if(vis[now]) continue; //BFS的判重
if(fir.dp==0){
vis[0]=true;
cout << fir.step << endl;
return ;
}
//如果到达,输出并退出BFS
Q.push(fir); //新状态入队
vis[now] = true; //记录状态,用于判重
}
}
}
int main(){
cin >> N >> M;
int temp=(1<<(N))-1;
for(int i=1; i<=M; i++){
for(int j=1; j<=N; j++){
cin >> a[i][j];
}
}
BFS(temp);
if(!vis[0])
cout << -1 << endl; //无法到达,输出-1
return 0;
}
这题最后的难点就是位运算了其实笔者自己也有点懵......
日后笔者会潜心研究并撰写相关专题的文章的
还请大家耐心等待
如有疑问欢迎在评论区提出
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。