状态压缩DP入门

状态压缩DP

    大体谈一下为什么叫做状态压缩DP,是因为有些问题的一个子问题就有有很多的状态,如果直接保存按照一般的线性DP来做,数组往往开的太大了!所以呢,咱们就可以把一个状态压缩成一个十进制的数,通俗的举个栗子:
如果说:有5个门,1代表开,0代表关。那么:0 1 1 0 1 就代表 关 开 开 关 开  可以用一个十进制的数字13来代替,这样就把一个状态为  (关 开 开 关 开)压缩成 --》 13.

状态压缩DP基础知识讲解

    如果想学会状压DP,必需要掌握一些基本的位运算的知识,因为压缩状态往往需要通过二进制(根据题目要求用不同的进制压缩状态,大多是二进制就可以解决,根据题目要求灵活运用)的转换,来压缩。

    首先介绍一下运算符号:
1、 & 与   -->  a & b  只有 a b 都等于 1 ,a & b == 1
栗子:            10101
                   &   1001    
                 == 00001 
2、| 或     --> a | b 只要 a 与 b 有一个是1 ,a | b == 1
荔枝:          10101
                 |     1001    
               ==  11101
3、^ 异或 --> a ^ b 只要 a b 不相同 , a ^ b == 1
励志:          10101
                 ^    1001    
               ==  11100
4、>> 向后进位    5 的二进制为 :101  ,5>>1 == 010  >>的作用就是  把   一个数对应的二进制的最后一个二进制位抹去,或者也可以理解为每个二进制位都向右平移一位,而最右边的一位数被挤了出来,挤没了。(其实>>1 相当于除以 2 , >>2 相当于除以4,用位运算会比 “ / ” 直接除更快呦!)
5、<< 向前进位    5 的二进制为 :101  ,5<<1 == 1010  <<的作用就是  在   一个数对应的二进制的最后一个二进制位后面加一个0,或者也可以理解为每个二进制位都向左平移一位,而最右边的一位数被空出来了,用0补上。(其实<<1 相当于乘以 2 , <<2 相当于乘以4,用位运算会比 “ * ” 直接除更快呦!)

例题:

    学会了状态压缩需要的基础知识后,咱们通过一个简单的小例题说明怎么来用状态压缩DP算法。
先附上题目的链接,学会后,自己动手尝试AC呦:http://poj.org/problem?id=3254
 

Corn Fields

Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 19733   Accepted: 10344

Description

Farmer John has purchased a lush new rectangular pasture composed of M by N (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 and N
Lines 2..M+1: Line i+1 describes row i of the pasture with N 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 3

1 1 1

0 1 0

Sample Output

9

Hint

1 2 3

   4

Number the squares as follows:
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.

Source

USACO 2006 November Gold

[Submit]   [Go Back]   [Status]   [Discuss]

 

 我来给大家描述一下吧,毕竟看中文还是快(hiahiahia~~~)
题目描述:给你M * N 块的土地,让你在每一小块地上种
。。。。。。现在很无语。。
昨天晚上写了将近2小时快写完了,然后点了发布博客,在题目描述:给你M * N 块的土地,让你在每一小块地上种这句话后面的博客全部消失了!!! 烦了一个小时。 然后!今天又认认真真写了一个晚上!终于写完了,心里暗喜觉得只要会c++的都能看懂吧~ 然后点了保存修改,发布博客,然后!! 又出现了昨天晚上的情况。。 这回是真崩溃了。。。 博主已哭晕n次,心里不甘!!! 于是找原因,发现当粗 我想给大家更好的视觉效果,于是用了
这个玉米图标,所以种后面的所有的东西都消失了,在这提醒一下大家,以后写博客最好不要用输入法里的图==(不作不会死。。。)
之前给同学讲过2遍这个例题,我又写了两边这个题解加DP入门的思想与讲解还有总结,,,现在都没了。。
最进一段时间是没有心情写了,想到这个题都恶心了,于是在这吐槽一下,最后只能勉强附上这个题的AC代码了,。。。。
 

//http://poj.org/problem?id=3254
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int M, N, l;
long long land[30];
long long state[30];
long long dp[30][10000];
int main()
{
    scanf("%d %d", &M, &N); //输入
    for(int i = 1; i <= M; i++){
        for(int j = 1; j <= N; j++){
            scanf("%d", &l);
            if(l == 1) land[i] += (1 << (N - j));
//   通过位运算计算该行的状态对应的数字,大家可以自己举个栗子试试就明白了,
//   为什么这么计算,其实就相当于给你一串 为二进制数,
//   让你转换成十进制,大家也可以用自己的方法来转换。
        }
    }


    int len = 1; //记者点有多少个符合调节的状态
    for(int i = 0; i < (1 << N); i++){  //把所有的可能的状态遍历一遍
        if((i & (i << 1)) == 0){  //判断是否符合咱们的要求(不可以相邻)
            state[len++] = i;  //存下来,这样把一个状态压缩成一个数后就好存多了吧!
        }
    }



    for(int i = 1; i < len; i++){
        if((land[1] & state[i] ) == state[i])
            dp[1][i] = 1;
    }


    for(int i = 2; i <= M; i++){ // 从第 2 行一直到第 M 行
        for(int j = 1; j < len; j++){  // 先看一下第 i 行的所有可以种植的状态

            if((land[i] & state[j]) != state[j]) continue;  // 如果不能种,那么返回,继续找。

            for(int k = 1; k < len; k++){ //当第i行的state[j]状态符合第i行土地的时候
//看一下 i 行的前一行的所有状态,

                if((land[i - 1] & state[k]) != state[k]) continue;
//当第i - 1行的state[k]状态符合第i - 1行土地的时候
                if(state[j] & state[k])                  continue;
//当第i行于第i-1行种植方法不相邻的时候,如果有上下紧挨着的,那么 state[j] & state[k] 肯定大于0,
//大于0就不符合,所以continue。
//当第i行的一个种植方法 符合第i行的土地的时候,而且 第i-1的一个种植方法也符合其i-1行的土地,而且
//第i行于第i-1行也没有相临的元素的时候 让第i行的第j状态的 可以种植的方法数目就等于它自己加上前一行当状态为k的时候的方法数。
                dp[i][j] = (dp[i][j] + dp[i - 1][k]) % 100000000;//按题目要求,得取余
            }
        }
    }

    long long ans = 0;
    for(int j = 1; j < len; j++)
            ans = (ans + dp[M][j]) % 100000000;
    printf("%lld\n" ,ans );
    return 0;
}

 

发布了84 篇原创文章 · 获赞 170 · 访问量 4万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览