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);
}

 

posted @ 2018-11-07 16:03  jiangminghong  阅读(171)  评论(0编辑  收藏  举报