POJ3254&&洛谷P1879 [USACO06NOV]玉米田Corn Fields

状压DP入门题

我写的第一道状压DP
状压就是把某种状态,用某种进制的数字串表示出来,比如说一行格子,奇数格子有 障碍物,偶数没有,我们就可以表示成1010101,这大概就是状压的主要思想
对于这些数字串,我们叫做可行状态,需要预处理出来,然后我们发现,预处理所有状态,可以用dfs搜一遍,就可以很快的得到所有可行状态
对于这道题,可行状态就是在二进制表示中两个1不挨着,如何知道二进制表示不挨着?将串向左移动一位,把每位错开,按位&一下,是0则没有重叠部分,就是可行解,然后我们预处理一下在m个格子内有多少种可行状态

if((i&(i<<1))==0) s[++cnt]=i

然后就是对初始状态的预处理,定义f[i][j]表示第i行的状态为j的方案数,我们先预处理第一行可行的状态k,然后f[i][k]=1,如何确定是否可行?因为所有可行状态都已经处理完了,所以每行可行的唯一条件就是不用有0的 位置,所以我们按位与一下,我们先预处理了每行的什么位置有0,然后按位与的话,可行就会是0,然后就没了
最后就是转移,每一行的状态只与上一行有关,所以只要和上一层的状态按位与是0就说明没有上下相邻的方格,所以我们枚举当前行遇上一行的状态,对于可行状态转移一下就好了

代码

//By AcerMo
#include
#include
#include
#include
#include
using namespace std;
const int mod=1e8;
int n,m;
int cnt,s[2050],e[20],f[20][2050];
inline int read()
{
	int x=0;char ch=getchar();
	while (ch>'9'||ch<'0') ch=getchar();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x;	
}
inline void dfs()
{
	int su=1<<m;
	for (int i=0;i<su;i++)
	if (!(i&(i<<1))) s[++cnt]=i;
	return ;
}
inline bool che(int x,int y)
{
	return (s[x]&e[y])==0;
}
signed main()
{
	n=read();m=read();dfs();
	for (int i=1;i<=n;i++)
	for (int k=1;k<=m;k++)
	{
		int x=read();
		if (!x) e[i]+=(1<<(m-k));
	}
	for (int i=1;i<=cnt;i++)
	if (che(i,1)) f[1][i]=1;
	for (int i=2;i<=n;i++)
	for (int k=1;k<=cnt;k++)
	if (che(k,i)) 
	for (int j=1;j<=cnt;j++)
	if (che(j,i-1)&&(s[j]&s[k])==0)
	f[i][k]=(f[i][k]+f[i-1][j])%mod;
	int ans=0;
	for (int i=1;i<=cnt;i++)
		ans=(ans+f[n][i])%mod;
	cout<<ans;
	return 0;
}

你可能感兴趣的:(动态规划)