状态压缩dp 状压dp 详解

说到状压dp,一般和二进制少不了关系(还常和博弈论结合起来考,这个坑我挖了还没填qwq),二进制是个好东西啊,所以二进制的各种运算是前置知识,不了解的话走下面链接进百度百科

https://baike.baidu.com/item/%E9%80%BB%E8%BE%91%E8%BF%90%E7%AE%97/7224729?fr=aladdin

现在我就当你明白了所有前置知识点了

状压dp就是通过一系列操作(例如用二进制)复杂的状态进行压缩,然后转移

现在我们来一道板子题感受一下状压dp

https://www.luogu.org/problemnew/show/P1879

看这个题很明显就可以用二进制状压,1表示种,0表示不种

但是我们要进行状态的合法判断

根据这个题的题目,我们发现,相邻位置上有两个1是不合法的,在不能种草的地上种草是不合法的,那么如何解决呢,给点时间自己想一想吧

相邻位上有两个1,那么我们可以把原数左移一位再和自己去&,如果结果大于0,就说明存在相邻位上有两个1,不合法,若等于0即为合法

证明也很简单,举几个例子就能理解了

到这里左右判断就搞定了,还有上下呢?

明白了左右,上下就更简单了,上一行和当前行也取&,同样大于0不合法,因为如果有一位上为1,那么就说明这两行在同一列上都有1,是不合法的

最后是和原图的01判断,也很简单,只需要和原图取&,若结果等于当前行的状态即为合法,反之不合法

证明:若结果不为当前状态则说明,在某一位置,当前状态为1,原图为0,所以不成立

 好啦,问题都解决了,我们上代码吧

#include
#include
using namespace std;
int n,m;
long long ans;
int land[20][20],la[20],able[(1<<12)+3];//数组注意大小
long long f[20][(1<<12)+3];
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&land[i][j]);
            la[i]=(la[i]<<1)+land[i][j];
        }
    }
    for(int i=0;i<(1<)
    {
        if(!(i&(i<<1)))
        {
            able[i]=1;
        }
    }
    f[0][0]=1;
    for(int i=1;i<=n;i++)//第i行 
    {
        for(int j=0;j<(1<//状态为j 
        {
            if(able[j]&&((j&la[i])==j))
            {
                for(int k=0;k<(1<//枚举上一行状态
                {
                    if(!(j&k))
                    {
                        f[i][j]+=f[i-1][k];
                        f[i][j]%=100000000;
                    }
                }
            }
        }
    }
    for(int i=0;i<(1<)
    {
        ans+=f[n][i];
        ans%=100000000;
    }
    printf("%lld",ans);
}

感觉如何??再来道题练练手

https://www.luogu.org/problemnew/show/P2704

转载于:https://www.cnblogs.com/xiaojuA/p/9842755.html

你可能感兴趣的:(状态压缩dp 状压dp 详解)