洛谷 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 }