說到狀壓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
