BZOJ1725 [Usaco2006 Nov]Corn Fields牧场的安排
题目描述:
Farmer John新买了一块长方形的牧场,这块牧场被划分成M列N行(1<=M<=12; 1<=N<=12),每一格都是一块正方形的土地。
FJ打算在牧场上的某几格土地里种上美味的草,供他的奶牛们享用。遗憾的是,有些土地相当的贫瘠,不能用来放牧。
并且,奶牛们喜欢独占一块草地的感觉,于是FJ不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。
当然,FJ还没有决定在哪些土地上种草。 作为一个好奇的农场主,FJ想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择。
当然,把新的牧场荒废,不在任何土地上种草,也算一种方案。请你帮FJ算一下这个总方案数。
题解:
看到数据范围我们可以向状压dp上想。
状压dp看起来很深奥,其实只是一个高级的二进制枚举而已。
我们将十进制转成二进制,要用到位运算。
对于此题我们可以推出dp方程:
f[i][j]表示第i行,此行现在到了第j个位置的方案数。
那么:
f[i-1][k]表示i的上一行的状态
f[i][j]=f[i-1][k]+f[i][j](0<=k<=maxx)
具体见代码。
附上代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<vector> #include<cmath> using namespace std; #define mod 100000000 int m,n,a[20][20],dp[20][1<<13]; long long f[20],maxx,ans; bool flag[1<<13]; int main() { scanf("%d%d",&m,&n); for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]); for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) f[i]=(f[i]<<1)+a[i][j];//每一行的初始状态,即此位置能否种草 maxx=(1<<n)-1;//处理出每行二进制下最多的位数 for(int i=0;i<=maxx;i++) if((((i<<1)&i)==0)&&(((i>>1)&i)==0))//如果此位的前一位不能放,且后一位也不能放,那么此位就能放 flag[i]=1;//相当于处理相邻草地的情况 for(int i=0;i<=maxx;i++) if(flag[i]==1&&((i&f[1])==i))//第一行的状态(此位能放且草地不贫瘠) dp[1][i]=1; for(int i=2;i<=m;i++)//枚举行 for(int j=0;j<=maxx;j++)//枚举列 if(flag[j]==1&&((j&f[i])==j))//当前行当前位能放且草地不贫瘠 for(int k=0;k<=maxx;k++)//枚举上一行的情况,即处理上下不相邻的情况 if((j&k)==0) dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;//转移方程 for(int i=0;i<=maxx;i++)//统计答案 ans=(ans+dp[m][i])%mod; printf("%lld",ans); }