洛谷P1879 [USACO06NOV]玉米田Corn Fields(状压DP入门题)

位运算

学习状压之前必须要熟练掌握位运算

位运算名 符号 效果
&(and) 按位与 如果两个相应的二进制位都为1,则该位的结果值为1,否则为0
l(or) 按位或 两个相应的二进制位中只要有一个为1,该位的结果值为1
^(xor) 按位异或(单身狗操作) 若参加运算的两个二进制位值相同则为0,否则为1
~ 取反 一元运算符,用来对一个二进制数按位取\反,即将0变1,将1变0
<< 左移 用来将一个数的各二进制位全部左移N位,右补0
>> 右移 将一个数的各二进制位右移N位,移到右端 的低位被舍弃,对于无符号数,高位补0

**下面是一些基本操作
洛谷P1879 [USACO06NOV]玉米田Corn Fields(状压DP入门题)
题目描述
农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地。John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。

遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。

John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)

输入输出格式

输入格式:
第一行:两个整数M和N,用空格隔开。

第2到第M+1行:每行包含N个用空格隔开的整数,描述了每块土地的状态。第i+1行描述了第i行的土地,所有整数均为0或1,是1的话,表示这块土地足够肥沃,0则表示这块土地不适合种草。

输出格式:
一个整数,即牧场分配总方案数除以100,000,000的余数

代码

#include<bits/stdc++.h>
#define mod 100000000
using namespace std;
const int M=15,N=1<<15;
int st[N],mp[M]//存可行的不相邻的状态以及图中每排的状态;
int dp[M][N];
int n,m;
int ans;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f; 
}
bool judge1(int x)//判断相邻两点是否冲突
{
	return (x&(x<<1));
}
bool judge2(int i,int x)//判断点能不能放
{
	return (mp[i]&st[x]);
}
int main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++)
	{
	for(int j=1;j<=m;j++)
	{
		int a=read();
		if(a==0)  
        mp[i]+=(1<<(j-1)); 
        /*
        相当于无符号取反操作如将0 1 0变为1 0 1。
        在判断能不能放做准备
        */
    }
    }
	int k=0;
	for(int i=0;i<(1<<m);i++)
	{
		if(!judge1(i))
		st[++k]=i;
	}
	for(int i=1;i<=k;i++)
	{
		if(!judge2(1,i))
		dp[1][i]=1;
	}
	for(int i=2;i<=n;i++)
	{
		for(int j=1;j<=k;j++)
		{
			if(judge2(i,j))
			continue;
			for(int f=1;f<=k;f++)
			{
				if(judge2(i-1,f))continue;//剪枝
				if(!(st[j]&st[f]))//判断上下是否冲突
				dp[i][j]+=dp[i-1][f];	
			}
		}
	} 
	for(int i=1;i<=k;i++)
	{
		ans+=dp[n][i];
		ans%=mod; 
	}
	printf("%d",ans);
	return 0;
}