狀態壓縮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<iostream>
#include<cstdio>
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<<m);i++)
    {
        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<<m);j++)//狀態為j 
        {
            if(able[j]&&((j&la[i])==j))
            {
                for(int k=0;k<(1<<m);k++)//枚舉上一行狀態
                {
                    if(!(j&k))
                    {
                        f[i][j]+=f[i-1][k];
                        f[i][j]%=100000000;
                    }
                }
            }
        }
    }
    for(int i=0;i<(1<<m);i++)
    {
        ans+=f[n][i];
        ans%=100000000;
    }
    printf("%lld",ans);
}

感覺如何??再來道題練練手

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM