状态压缩入门
洛谷P2622 关灯问题II
关灯问题——状态压缩经典 所谓状态压缩
就是将问题可能遇到的每一个状态用一个唯一的二进制数表示
其复杂度一般都是指数级的 这也注定了状压类的题数据规模都不会太大
此题中我们以1表示开灯状态,0表示关灯状态
这样我们可以以一个长度为n的二进制数唯一的表示每个状态
接着就可以依靠既定的开关关系将每个状态连接起来
通过BFS解决
以灯全开状态为起点
二进制为n个1 ,十进制表示为(1<< n)-1
开始枚举m个开关以得到接下来的m个状态
for(int i=1;i<=m;i++) { ss=(1<< n)-1; for(int j=1;j<=n;j++) { if( a[i][j]==1 && (ss&(1<<j-1)) ) ss^=(1<<j-1); else if( a[i][j]==-1 && !(ss&(1<<j-1)) ) ss|=(1<<j-1); } }
ss&(1<< j-1)此运算用以检查当前状态的第j位是否为1
1<< j-1意思是将1左移j-1位,移动后只有第j位上是1
若开关操作为1且当前状态第j位是1
则ss^=(1<< j-1) 表示 1与1异或后得0
若开关操作为-1且当前状态第j位是0
则ss|=(1 << j-1) 表示 1或0 后得1 (此处异或操作也对,因为1异或0得1)
之后便用相同的方法在得到的每个状态上再不断搜索每个状态
由于是BFS,所以一旦某一步状态表示为0(十进制二进制的0写法都是0)
则当前步数必定是最短操作数
若遍历完所有状态都没有0,则输出-1
记得搜索过的状态要打vis标记避免重复访问
#define ll long long #define rep1(i,a,b) for(int i=a;i<=b;i++) #define rep2(i,a,b) for(int i=a;i>=b;i--) #include <iostream> #include <queue> using namespace std; const int e = (int)105; int m,n; int a[e][e]; int vis[1000010]; struct node { int u; int s; }; int bfs(){ queue<node>q; int ss ; vis[(1<<n)-1]=1; q.push((node){(1<<n)-1,0}); while(!q.empty()){ node aa=q.front(); q.pop(); // cout << aa.u << '\n'; if(aa.u==0) return aa.s; rep1(i,1,m){ ss=aa.u; rep1(j,1,n){ // cout << aa.u << '\n'; if(a[i][j]==1&&(ss&(1<<j-1))) ss^=(1<<j-1); else if(a[i][j]==-1&&!(ss&(1<<j-1))) ss|=(1<<j-1); } if(!vis[ss]) { // cout << aa.u << "***" << '\n'; q.push((node) {ss,aa.s+1}); vis[ss]=1; } } } return -1; } int main(int argc, char const *argv[]) { ios::sync_with_stdio(false); cin >> n >> m; rep1(i,1,m){ rep1(j,1,n){ cin >> a[i][j]; } } cout << bfs() << '\n'; return 0; }
人十我百 人百我万