从状态压缩的特点来看,这个算法适用的题目符合以下的条件:
1.解法需要保存一定的状态数据(表示一种状态的一个数据值) ,每个状态数据通常情况下是可以通过 2 进制来表示的。这就要求状态数据的每个单元只有两种状态,比如说棋盘上的格子,放棋子或者不放,或者是硬币的正反两面。这样用 0 或者 1 来表示状态数据的每个单元,而整个状态数据就是一个一串 0 和 1 组成的二进制数。2.解法需要将状态数据实现为一个基本数据类型,比如 int, long 等等, 即所谓的状态压缩。状态压缩的目的一方面是缩小了数据存储的空间,另一方面是在状态对比和状态整体处理时能够提高效率。这样就要求状态数据中的单元个数不能太大,比如用 int 来表示一个状态的时候,状态的单元个数不能超过 32(32 位的机器)。
二、动态规划
如果说状态压缩是数据结构的话,那么动态规划应该是算法了。题目通过动态规划来解通常有两个动机,第一是利用递归的重叠子问题,进行记忆话求解,即递归法的优化。第二是把问题看作多阶段决策的过程来求解问题。在状态压缩动态规划中我们讨论的是第二种动机。
多阶段决策过程求解问题的动态规划最重要的是划分阶段和找到状态转移方程。 对于划分阶段,是根据不同阶段之间的独立性来划分,通常会用状态数组的第一个下标来记录这个阶段的标记(比如 01 背包问题中的状态数组第一个下标为物品的个数,棋盘放棋子问题中的状态数组的第一个下标为棋盘的行数等等)。另一个重要的便是状态转移方程,状态转移方程是递推时得到一个状态数据的重要根据。通常情况下状态数组的除了第一个下标以外都是表示状态数据的,而状态数组的值是和所求结果紧密结合的。在后面的几个例题中会重点说明
状态转移方程。
当状态压缩和动态规划结合的时候便形成了一类问题的一种算法,即状态压缩动态规划的算法。这种算法最常见在棋盘问题上或者网格问题上,因为这一类问题的状态数据的单元较少,可以通过状态压缩来对当前棋盘或者网格的状态进行处理。
三、实例
说了这么多应该上菜啦
题:用1x2的瓷砖覆盖NxM的地板,问总共有多少种方案
首先应该满足N*M是偶数,这是一个大前提,因为每一个小瓷砖的面积是偶数。
分析:总体思路---先将第一行所有状态进行遍历,并采用数据压缩的方法记录有效状态,这里用0表示没有横向瓷砖覆盖---有下一行的瓷砖纵向覆盖,1表示有瓷砖覆盖---横向或纵向,用一个二维数组来记录能够到达当前状态的方案数,行数作为一维索引,状态值作为二维索引;然后从i-1行的有效状态出发,遍历并判断第i行的所有状态是否与i-1行的有效状态兼容,如果兼容,则将当前遍历的i行k状态的可到达方案数加上与其兼容的i-1行j状态的可到达方案数。
相关说明:
是否覆盖---10101 则i行0,2,4位置被覆盖,1,3位置未被覆盖,需要下一行的瓷砖纵向覆盖
兼容性------i-1行00110与i行11111兼容,与i行10111不兼容
该问题里状态转移就是兼容性判断
详见状态压缩动态规划 POJ 2411 (编程之美-瓷砖覆盖地板)分析
code
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
int w,h;
bool TestFirstLine(int n)
{
int i=0;
while(i<w)
{
if( n & (0x1<<i) )
{
if(i==w-1 || ( n &0x1<<(i+1) )==0 )
{
return false;
}
i+=2;
}
else
{
i++;
}
}
return true;
}
bool TestCompitable(int cur_s,int pre_s) // test if status (i, cur_s) and (i-1, pre_s) is compatable
{
int i=0;
while(i<w)
{
if(cur_s & (0x1<<i) )
{
if( !( pre_s & (0x1<<i) ) )
{
i++;
}
else if( ( pre_s & (0x1<<(i+1) )) && ( cur_s & 0x1<<(i+1) ) && i!=w-1 )
{
i+=2;
}
else
{
return false;
}
}
else
{
if( pre_s & (0x1<<i) )
{
i++;
}
else
{
return false;
}
}
}
return true;
}
int main(void)
{
int i,j,k;
int nStates;
long** s;
printf("Please enter the width of the floor:\n");
printf("-----");
scanf("%d",&w);
printf("Please enter the height of the floor:\n");
printf("-----");
scanf("%d",&h);
if((w*h)%2)
{
printf("The program number is: 0\n");
system("pause");
return 0;
}
if(w>h)
{
std::swap(w,h);
}
nStates=(1<<w);
s=(long**)malloc(h*sizeof(long*));
for(i=0;i<h;i++)
{
s[i]=(long*)malloc(nStates*sizeof(long));
memset(s[i],0,nStates*sizeof(long));
}
for(i=0;i<nStates;i++)
{
if(TestFirstLine(i))
{
s[0][i]=1;
}
}
for(i=1;i<h;i++)
{
for(j=0;j<nStates;j++) //cur-line,state index
{
for(k=0;k<nStates;k++) //pre-line,state index
{
if(s[i-1][k]!=0) //pre-line has the state
{
if(TestCompitable(j,k))
{
s[i][j]+=s[i-1][k]; //compatible
}
}
}
}
}
printf("The program number is: %d\n",s[h-1][nStates-1]);
system("pause");
return 0;
}
状态压缩与动态规划(DP)---编程之美---瓷砖覆盖地板---POJ2411,布布扣,bubuko.com
状态压缩与动态规划(DP)---编程之美---瓷砖覆盖地板---POJ2411
原文:http://blog.csdn.net/cjc211322/article/details/22720351