洛谷P1879玉米田(状压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的余数。

输入输出样例

输入样例#1:

 2 3
 1 1 1
 0 1 0

输出样例#1:

 9

\(Solution\):

这题需要较强的位运算基础,比如像我这种位运算渣渣就搞了好久的位运算才来做这题

首先,我们需要一个数组 \(g [ i ]\) 来记录每一行的状态, 即每一行有哪些位置可以种草

for(int i=1;i<=n;i++)//数组a[i][j]表示是否可以种草
    for(int j=1;j<=m;j++)
    {
        a[i][j]=read();
        g[i]=(g[i]<<1)+a[i][j];
        //g[i]的递推,二进制中1表示能种,0表示不能种
    }

对于每一行,最多有 \(2^m-1\) 种状态,但是其中有些状态就已经有两块相邻的地种了草,比如:11010010 。所以我们还需要排除每一行的所有状态中有相邻1的情况,即:

for(int i=0;i<mx;i++) v[i]=((i&(i<<1))==0)&&((i&(i>>1))==0);

这里的v[i]是bool数组,至于 \((\ (\ i\ \&\ (\ i<<1)\ )==0\ )\ \&\&\ (\ (\ i\ \&\ (\ i > > 1\ )\ ) = = 0\ )\) 本人也是搞了好久才弄懂qwq

\(( i<<1)​\) 表示把 i 左移了了一位,然后与上 i ,如果 i 里面有两个连续的1,那么 ( i & ( i < < 1 ) ) 的值就不会是0。例:i = 101100 ,所以 \(( i < < 1 ) = 1011000​\),然后:

i 0 1 0 1 1 0 0
i<<1 1 0 1 1 0 0 0
与运算 0 0 0 1 0 0 0

同理,当 i = 101100 时,( i > > 1 ) = 10110,它们做与运算:

i 1 0 1 1 0 0
i>>1 0 1 0 1 1 0
与运算 0 0 0 1 0 0

具体实现请见代码:

#include<bits/stdc++.h>
#define il inline
using namespace std;
const int md=1e8;
il int read(){
    int f=1,w=0;char c=0;
    while(!isdigit(c))
    {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(isdigit(c)) w=w*10+(c^48),c=getchar();
    return f*w;
}
int n,m,a[15][15],g[15],mx,f[15][4100],ans;
bool v[4100];
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            a[i][j]=read();
            g[i]=(g[i]<<1)+a[i][j];
        }//递推求g[i]
    mx=1<<m;//mx-1=状态数最大值
    f[0][0]=1;//初始值,第0行一块草也不种
    for(int i=0;i<mx;i++)
        v[i]=((i&(i<<1))==0)&&((i&(i>>1))==0);
        //排除每一行的所有状态中有相邻1的情况
    for(int i=1;i<=n;i++)//枚举行
        for(int j=0;j<mx;j++)//枚举这一行的状态
            if(v[j]&&(j&g[i])==j)
            //(j&g[i]):在g[i]中,1表示能种草;在j中,1表示要种草
                for(int k=0;k<mx;k++)
                    if((k&j)==0)
                //若上一行与这一行的某一列都是1,则与运算值不为0
                        f[i][j]=(f[i][j]+f[i-1][k])%md;
    for(int i=0;i<mx;i++)
        ans=(ans+f[n][i])%md;
    cout<<ans<<endl;
    return 0;
}
posted @ 2019-02-15 14:22  Uchiha__Itachi  阅读(117)  评论(0编辑  收藏  举报