先掛個連接 - 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
若有疑問歡迎在評論區提出搜索
留個贊再走唄~程序