洛谷 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
posted @ 2018-11-21 20:09  HHHyacinth  阅读(318)  评论(0编辑  收藏  举报