1. 程式人生 > >洛谷 P2622 關燈問題II(狀壓DP入門題)

洛谷 P2622 關燈問題II(狀壓DP入門題)

傳送門

https://www.cnblogs.com/violet-acmer/p/9852294.html

 

題解:

  相關變數解釋:

 1 int n,m;
 2 int a[maxn][20];//a[i][j] : 第i個開關對第j個燈的效果。
 3 bool vis[R(10)];//vis[i] : 判斷狀態i是否被訪問過
 4 struct Node
 5 {
 6     int status;//狀態
 7     int minTimes;//來到當前狀態按下開關的最小次數
 8     Node(int a=0,int b=0):status(a),minTimes(b){}
9 }; 10 queue<Node >myqueue;//用佇列中的狀態去解鎖其他為解鎖(訪問)過的狀態,並能保證被解鎖的狀態的minTimes最小

  步驟:

  (1):將Node( (1<<n)+1,0 ) 加入佇列,因為初始等全是亮的,對應到二進位制就是n個1,並且需要 0 次按下開關。

  (2):從隊頭依次彈出元素,並用當前狀態去解鎖其他狀態,並能保證被其解鎖的狀態的minTimes是最小的。

  (3):重複(2)過程,直到找到 0 狀態或佇列為空

AC程式碼:

 1 #include<iostream>
 2 #include<cstdio>
 3
#include<queue> 4 #include<cstring> 5 using namespace std; 6 #define mem(a,b) memset(a,b,sizeof(a)) 7 #define R(x) (1<<x) 8 const int maxn=100+10; 9 10 int n,m; 11 int a[maxn][20];//a[i][j] : 第i個開關對第j個燈的效果。 12 bool vis[R(10)];//vis[i] : 判斷狀態i是否被訪問過 13 struct Node 14 { 15 int status;//
狀態 16 int minTimes;//來到當前狀態按下開關的最小次數 17 Node(int a=0,int b=0):status(a),minTimes(b){} 18 }; 19 queue<Node >myqueue;//用佇列中的狀態去解鎖其他為解鎖(訪問)過的狀態,並能保證被解鎖的狀態的minTimes最小 20 21 int nextStatus(int nowStatus,int i) 22 { 23 int x=nowStatus; 24 for(int j=1;j <= n;++j)//從右往左一一對應 25 { 26 if(a[i][j] == 1 && (x>>(j-1)&1))//一定要注意判斷x的第j為是否為1 27 x ^= (1<<(j-1));//^ : 相同為0,不同為1 28 else if(a[i][j] == -1) 29 x |= (1<<(j-1)); 30 } 31 return x; 32 } 33 int updataQ(Node node) 34 { 35 for(int i=1;i <= m;++i) 36 { 37 int status=nextStatus(node.status,i);//找到當前狀態node.status可以解鎖的下一狀態 38 if(!vis[status])//如果被訪問過,那麼其minTimes肯定要小於當前的minTimes+1 39 myqueue.push(Node(status,node.minTimes+1)),vis[status]=true; 40 if(status == 0)//判斷被解鎖的狀態是否為0狀態 41 return node.minTimes+1; 42 } 43 return 0; 44 } 45 int Solve() 46 { 47 mem(vis,false); 48 while(!myqueue.empty()) 49 myqueue.pop(); 50 myqueue.push(Node(R(n)-1,0));//步驟(1) 51 while(!myqueue.empty())//步驟(2)(3) 52 { 53 Node node=myqueue.front(); 54 myqueue.pop(); 55 int res=updataQ(node);//更新佇列中的狀態 56 if(res != 0)//判斷被解鎖的狀態是否有0狀態,如果有,直接輸出,一定是最小的按下次數 57 return res; 58 } 59 return -1; 60 } 61 int main() 62 { 63 scanf("%d%d",&n,&m); 64 for(int i=1;i <= m;++i) 65 for(int j=n;j >= 1;--j)//j : n down 1 用意:與二進位制的位數一一對應(從右往左) 66 scanf("%d",a[i]+j); 67 printf("%d\n",Solve()); 68 }
View Code