状态压缩讲解



*注:本文对状态压缩的描述非正式化,比较随意,意在让人容易理解,下面开始谈谈我对状态压缩的理解。


1.为什么要采用状态压缩?
采用状态压缩的主要原因是原状态不容易表示或者状态数目过多,内存不够用。


2.用状态压缩有什么好处?
当然自然解决了上面的两个问题-----状态容易表达,至于内存,用一个数的二进制表示状态可以节省很多内存空间(当然也有使用的局限性)

3.状态压缩的难点?
状压一般是用于状压BFS和状压DP,状压的主要难点就是怎么压缩状态,然后就是位运算的使用,位运算一定要熟练。下面介绍位运算
& ---- 按位与,可以将某个数的某二进制位置为0,也可以用于取出某个二进制位
| ---- 按位或,可以将某个数的某二进制位置为1.
~ ---- 非,将一个数的所有二进制位取反
^ ---- 异或,相同为0,不同为1


本文将从几个例题出发,讲解状压的方法及原理


以下例题题意不在描述,还请先点开链接读题
例题一:
HDU 1429  胜利大逃亡(续)
一看此题,就是BFS搜索,怎么搜呢?考虑到钥匙最多只有10把,很容易想到状态压缩,将每把钥匙对应一个二进制数的一位,这样状态就可以轻松表示了
那么为什么要状态压缩?原因很简单,因为到达任意一个点(z,y),Ignatius身上带的钥匙的种类数量都可能不同,应该是属于不同的状态.想到这点程序就不难写了
下面给出AC代码,希望先自己写,不会了再参考下
/*
author: tpbluesky
time:	2015年8月16日14:04:48   
题解:	状态压缩 
*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define inf 0x3f3f3f3f
#define eps 1e-8
#define sqr(x) ((x)*(x))
using namespace std;
typedef long long ll;
const int maxn = 22;

char mp[maxn][maxn];
int vis[maxn][maxn][1<<10], n, m , t; 
struct node
{
	int x, y, step, state; 
}st;

int dx[] = {1,-1,0,0};
int dy[] = {0,0,1,-1};

bool isok(int x,int y)
{
	if(x < 1 || y < 1 || x > n ||  y > m || mp[x][y] == '*')
		return false;
	return true;
}

int bfs()
{	
	queue q;
	memset(vis,0,sizeof(vis));
	vis[st.x][st.y][0] = 1;
	q.push(st);
	while(!q.empty())
	{
		node tp = q.front();
		q.pop();
		if(mp[tp.x][tp.y] == '^')
			return tp.step;
		//cout<<"              "<= 'A' && mp[temp.x][temp.y] <= 'J')
				{
					int t = mp[temp.x][temp.y] - 'A';
					if(temp.state&(1<= 'a' && mp[temp.x][temp.y] <= 'j')
				{
					vis[temp.x][temp.y][temp.state]= 0;
					int t = mp[temp.x][temp.y] - 'a';
					temp.state |= (1<= t)
			printf("-1\n");	
		else
			printf("%d\n",ans);
	}
    return 0;
}


练习:
HDU 5094 --- Maze
HDU 1044 --- Collect More Jewels


例题二:
POJ 1324 Holedox Moving
刚看完题目,可能会感觉无从下手,状态是蛇所在位子的每个坐标,这种状态实在不容易表示,坐标太多,空间不够,怎么办,这时我们也很容易想到二进制表示
状态,但仍然不是很方便表示,经过观察,我们发现每个点相对前个点的相对方向其实很容易表示的,用二位二进制位正好表示四个方向,这样我们很容易从蛇头推出
每个蛇身的坐标,转移其实比较特别,直接移位就行了,不清楚的自己在纸上画画看,想到这点已经程序就不是很难写了,但是实现的时候需要一定技巧,并且能很熟练使 用位运算

下面还是给出AC代码,代码附带注释

/*
author: tpbluesky
time:   2015年8月16日21:57:40
题解:	状压BFS
*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define inf 0x3f3f3f3f
#define eps 1e-8
#define sqr(x) ((x)*(x))
using namespace std;
typedef long long ll;
const int maxn = 22;

int vis[maxn][maxn][1<<14];		//标记状态 
int mp[maxn][maxn];
int n, m, k;
int lor[] = {0,1,2,3};//up,down,left,right 	此处的方向和下面数组的方向是一致的,便于操作 
int dx[] = {1,-1,0,0};
int dy[] = {0,0,1,-1};
struct node
{
	int x, y, state, step;
}st;
int pos[10][2];

bool isok(int x,int y,int orx, int ory, int s)
{
	if(x < 1 || y < 1|| x > n || y > m || mp[x][y] == 1)
		return false;
	int t = 3;
	//cout<<"       "<>(i*2))&t; 		//将s的第i*2+1,i*2+2移到末尾,& 011,得到这两位的数,就可以推出方向 
		if(p == 0)	orx -= 1;
		if(p == 1)	orx += 1;
		if(p == 2)  ory -= 1;
		if(p == 3)  ory += 1;
	//	cout<<"        "< q;
	q.push(st);
	vis[st.x][st.y][st.state] = 1;
	int res = -1;
	while(!q.empty())
	{
		node tp = q.front();
		q.pop();
		if(tp.x == 1 && tp.y == 1){
			res = tp.step;
			break;
		}
		for(int i = 0;i < 4;++i)
		{
			node temp;
			temp.x = tp.x + dx[i], temp.y = tp.y+dy[i], temp.step = tp.step+1, temp.state = (tp.state<<2)|lor[i];  
			int t = (1<<(2*k-2))-1; temp.state &= t;	//上面的右移比较巧妙,可以自己动手模拟一下看看,此处是将temp.state高位全部置为0 
			//cout<



练习:
POJ 1184 --- 聪明的打字员
HDU 1043 --- Eight


看完状压BFS,状压DP待续。。。。。

你可能感兴趣的:(学习笔记)