若集合大小不超过N,集合中每个元素都是小于K的自然数,则我们可以把这个集合看作
一个N位K进制数,以一个【0,K^N-1】之间的十进制整数的形式作为DP状态的一维。
for(int i=0; i < (1<<n) ; i++) ;
//相当于枚举了所有的选与不选的情况
1.判断一个数字x二进制下第i位是不是等于1。
方法:if(((1<<(i−1))&x)>0) if(((1<<(i−1))&x)>0)
将1左移i-1位,相当于制造了一个只有第i位上是1,其他位上都是0的二进制数。
然后与x做与运算,如果结果>0,说明x第i位上是1,反之则是0。
2.将一个数字x二进制下第i位更改成1。
方法:x=x|(1<<(i−1))x=x|(1<<(i−1))
证明方法与1类似,此处不再重复证明。
3.把一个数字二进制下最靠右的第一个1去掉。
方法:x=x&(x−1)x=x&(x−1)
题目描述
现有n盏灯,以及m个按钮。每个按钮可以同时控制这n盏灯——按下了第i个按钮,
对于所有的灯都有一个效果。按下i按钮对于第j盏灯,是下面3中效果之一:
如果a[i][j]为1,那么当这盏灯开了的时候,把它关上,否则不管;
如果 -1,如果这盏灯是关的,那么把它打开,否则也不管;
如果 0,无论这灯是否开,都不管。
现在这些灯都是开的,给出所有开关对所有灯的控制效果,求问最少要按几下按钮才能全部关掉。
输入格式:
前两行两个数,n m
接下来m行,每行n个数,a[i][j]表示第i个开关对第j个灯的效果。
输出格式:
一个整数,表示最少按按钮次数。如果没有任何办法使其全部关闭,输出-1
这题需要对状压及位运算有一定的了解:首先要判断某一位的灯是开的还是关的,才能进行修改。
具体解法是:对队首的某一状态,枚举每一个开关灯操作,记录到达这一新状态的步数
(也就是老状态 + 1),若是最终答案,输出,若不是,压入队列。
也就是说:我们把初始状态,用每个操作都试一遍,就产生了许多新的状态,再用所有操作一一
操作新状态,就又产生了新的新状态,我们逐一尝试,直到有目标状态为止,这可以通过BFS实现。
所以现在知道为什么状压比较暴力了吧。
#include<iostream>
#include<vector>
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
int RD(){
int out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const int maxn = 2048;
int num,m,numd;
struct Node{
int dp,step;
};
int vis[maxn];
int map[maxn][maxn];
void BFS(int n){
queue<Node>Q;
Node fir;fir.step = 0,fir.dp = n;//初始状态入队,使灯的值都为一,
Q.push(fir);
while(!Q.empty()){//BFS
Node u = Q.front();
Q.pop();
int pre = u.dp;
for(int i = 1;i <= m;i++){//枚举每个操作
int now = pre;
for(int j = 1;j <= num;j++){
if(map[i][j] == 1){
if( (1 << (j - 1)) & now){
now = now ^ (1 << (j - 1));//对状态进行操作
}
}
else if(map[i][j] == -1){
now = ( (1 << (j - 1) ) | now);//对状态进行操作
}
}
fir.dp = now,fir.step = u.step + 1;//记录步数
if(vis[now] == true){
continue;
}
if(fir.dp == 0){//达到目标状态,都为零时为全灭
vis[0] = true;//相当于一个标记flag
cout<<fir.step<<endl;//输出
return ;//退出函数
}
Q.push(fir);//新状态入队
vis[now] = true;//表示这个状态操作过了(以后在有这个状态就不用试了)
}
}
}
int main(){
num = RD();m = RD();
int n = (1 << (num)) - 1;
for(int i = 1;i <= m;i++){
for(int j = 1;j <= num;j++){
map[i][j] = RD();
}
}
BFS(n);
if(vis[0] == false)
cout<<-1<<endl;
return 0;
}