先挂个连接 - https://www.luogu.com.cn/prob...c++
题面 - 现有N盏灯,M个按钮。每一个按钮能够同时控制这n盏灯——按下某个按钮,对于全部的灯都有一个效果。给出全部开关对全部灯的控制效果,问最少要按几下按钮能将灯从全开变为全关数组
Now,进入正题spa
瞧下数据,N<=10,M<=100
数据规模不很大,果断BFS(广度优先搜索)
然而......
BFS的判重又成了大问题
逐个比对,看状态是否一致,显然超时
不判重,BFS就直接废了设计
这时候,新Get到的技能——状态压缩闪亮登场
状态压缩即将原来状态的数组存放变为一个01构成的整数存放
这样就这能够设置一个下标表明状态的vis[]数组来判重
判重瞬间降到O(1)级别
状态的改变则用位运算,也降到O(1)级别code
就是这么容易......ci
下面挂上程序(C++)和精心设计的注解方便理解get
//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; }
这题最后的难点就是位运算了其实笔者本身也有点懵......
往后笔者会潜心研究并撰写相关专题的文章的
还请你们耐心等待it
若有疑问欢迎在评论区提出搜索
留个赞再走呗~程序