状态压缩DP

来源:互联网 发布:c语言strncpy 编辑:程序博客网 时间:2024/06/01 10:53
Corn Fields
Time Limit: 2000MS Memory Limit: 65536KTotal Submissions: 13320 Accepted: 6984

Description

Farmer John has purchased a lush new rectangular pasture composed of M byN (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can't be planted. Canny FJ knows that the cows dislike eating close to each other, so when choosing which squares to plant, he avoids choosing squares that are adjacent; no two chosen squares share an edge. He has not yet made the final choice as to which squares to plant.

Being a very open-minded man, Farmer John wants to consider all possible options for how to choose the squares for planting. He is so open-minded that he considers choosing no squares as a valid option! Please help Farmer John determine the number of ways he can choose the squares to plant.

Input

Line 1: Two space-separated integers: M andN
Lines 2..M+1: Line i+1 describes row i of the pasture withN space-separated integers indicating whether a square is fertile (1 for fertile, 0 for infertile)

Output

Line 1: One integer: the number of ways that FJ can choose the squares modulo 100,000,000.

Sample Input

2 31 1 10 1 0

Sample Output

9

Hint

Number the squares as follows:

1 2 3  4  


There are four ways to plant only on one squares (1, 2, 3, or 4), three ways to plant on two squares (13, 14, or 34), 1 way to plant on three squares (134), and one way to plant on no squares. 4+3+1+1=9.

//第一次学状态压缩DP,所以选择别人代码入门。

题意:给出一个n行m列的草地,1表示肥沃0表示贫瘠,现在要把一些牛放在肥沃的草地上,但是要求所有牛不能相邻,问你有多少种放法。

思路 A->B->C 在A 的条件得到 B的条件 ,而C和A条件没有相关性。

将状态分类  A  (i)  ={a1a2a3.*****an    } i    行的集合

                    A(i+1)={b1,  b2 b3 *****bn    } i+1行的集合

可知a1和b1相关 ,a2和b2相关,an和bn相关。


因为上下左右不能有相邻的牛。

1-----a1->b1,a2->b2,a3->b3*******an->bn

2-----a1->a2->a3->****an

先求满足2条件的组合状态

?????? -------问题来了怎么表示这个   集合状态

-------用二进制(列少)

??????--------问题来了怎么得到下一个集合状态

-------那么每个2的状态都能作为满足 下一行的前提条件                                        Then each of the 2 states can be used as a prerequisite to meet the next line

由此dp[I][j] 即可定义 第 i 行 状态为 j

转移方程 :  dp[i][j]=dp[i-1][k]  

那么可以想象dp[i-1][k]就是dp[i][j]的前提条件。

---------------------状态压缩DP------------------关键-------------位运算。

这个题目用到了 & 这个运算符。

用 x & (x<<1) 来判断一个数相邻两位是不是同时为1,假如同时为 1 则返回一个值,否则返回 0 ,这样就能优化掉一些状态

用 x & y 的布尔值来判断相同为是不是同时为1。

这里提下 位运算的两种形式 << 和>> 

x<<y其实就是  x * (2^y)

x>>y其实就是  取整 :x / (2^y)

1<<y 其实就是2^y

#include <cstdio>
#include <cstring>
const int N = 13;
const int M = 1<<N;
const int mod = 100000000;
int st[M],map[M];  ///分别存每一行的状态和给出地的状态
int dp[N][M];  //表示在第i行状态为j时候可以放牛的种数
bool judge1(int x)  //判断二进制有没有相邻的1
{
    return (x&(x<<1));
}
bool judge2(int i,int x)//用 x & y 的布尔值来判断相同为是不是同时为1。
{
    return (map[i]&st[x]);
}
int main()
{
    int n,m,x;
    while(~scanf("%d%d",&n,&m))
    {
        memset(st,0,sizeof(st));
        memset(map,0,sizeof(map));
        memset(dp,0,sizeof(dp)); //初始化
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++){
                scanf("%d",&x);
                if(x==0)
                    map[i]+=(1<<(j-1));//得到每一行的二进制状态编码 这里的0是二进制的1
                   //   2^j   + 0  +  ------ 2^i  
            }

        }
        int k=0;
//找到所有可行状态放到st数组栈中。
        for(int i=0;i<(1<<m);i++){
            if(!judge1(i))
                st[k++]=i;
        }
        for(int i=0;i<k;i++)//处理边界问题
        {
            if(!judge2(1,i))
                dp[1][i]=1;
        }
        for(int i=2;i<=n;i++)
        {
            for(int j=0;j<k;j++)
            {
                if(judge2(i,j))  //判断第i行 假如按状态j放牛的话行不行。//先和这行状态就行判断筛选
                    continue;
                for(int f=0;f<k;f++)
                {
                    if(judge2(i-1,f))   //剪枝 判断上一行与其状态是否满足
                        continue;
                    if(!(st[j]&st[f]))
                        dp[i][j]+=dp[i-1][f];
                }
            }
        }
        int ans=0;
        for(int i=0;i<k;i++){
            ans+=dp[n][i];
            ans%=mod;
        }
        printf("%d\n",ans);
    }
    return 0;
}

0 0