状压dp--P2622 关灯问题II

位运算:

  1. 1<< i >>1 & num,如果为1说明二进制下\(num\)的第\(i\)位为1,否则为0

  2. \(num_1\) & \(num_2\),如果为0说明二进制下两个数相同位下没有重叠的1

  3. \(num\) & \(num\)<<1,如果为0说明二进制下\(num\)的左右一位与它本身不同为1

定义:

状压 dp 是动态规划的一种,通过将状态压缩为整数来达到优化转移的目的。

一些方法:

  1. 如果上下行有影响的话,经常设\(dp_{i,j}\)\(i\)表示第\(i\)行,\(j\)表示这一行的状态,可以从\(dp_{i-1,k}\)转移,但是要判断是否合法

  2. 通常我们会把\(2^n-1\)中情况先预处理出当前行合法的状态,转移时再判断两行是否合法

  3. 如果我对一个状态有所操作,想得到另一个状态,那么我们可以对\(2^n-1\)种状态分别进行\(m\)次操作连边,跑最短路求出最小操作次数

例题

和我上面说的第二种处理方法一样。

code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a;
}
const int maxn = 200,inf = 1e9+7;
int n,m,a[maxn][2000];
struct node{
	int to,nxt;
}ed[maxn*maxn*100];
int head[maxn*maxn*100],tot;
void add(int u,int to){
	ed[++tot].to = to;
	ed[tot].nxt = head[u];
	head[u] = tot;
}
queue<int> q;
int dis[3000];
void BFS(int s){
	q.push(s);
	for (int i = 0;i < (1<<n);i++) dis[i] = inf;dis[s] = 0;
	while (!q.empty()){
		int x = q.front();q.pop();
		for (int i = head[x];i;i = ed[i].nxt){
			int to = ed[i].to;
			if (dis[to]!=inf) continue;
			dis[to] = dis[x] + 1;
			q.push(to);
			if (to == 0) return;
		}
	}
}
int main(){
	n = read(),m = read();
	for (int i = 1;i <= m;i++){
		for (int j = 1;j <= n;j++){
			a[i][j] = read();
		}
	}
	for (int i = 0;i < (1<<n);i++){
		for (int j = 1;j <= m;j++){
			int res = i;
			for (int k = 1;k <= n;k++){
				if (a[j][k] == 1&&(1<<k>>1)&i) res ^= (1<<k>>1);
				if (a[j][k] == -1&&!((1<<k>>1)&i)) res ^= (1<<k>>1);
			}
			add(i,res);
		}
	}
	BFS((1<<n)-1);
	if (dis[0] == inf){printf("-1\n");return 0;}
	printf("%d\n",dis[0]);
	return 0;
}
posted @ 2020-11-22 17:45  小又又yyyy  阅读(84)  评论(0编辑  收藏  举报