插頭dp學習筆記

2022-09-10 17:03:31 字數 1824 閱讀 5652

由於插頭dp很難懂於是又來記筆記了

插頭dp可以用來解決一些連通性狀壓問題。具體流程是分格仔處理,然後可以根據需要進行滾動,狀壓一下輪廓線狀態,常用4進製(?)之類的。

拿luogu例題做例子:

給出n*m的方格,有些格仔不能鋪線,其它格仔必須鋪,形成乙個閉合迴路。問有多少種鋪法?n,m(2<=n,m<=12)
對於此題只考慮格仔上方的插頭和格仔左方的插頭情況,然後特殊的對於當前輪廓線拐角(dp位置)有b1,b2表示右插頭和下插頭,然後在**制中,對於乙個線段有0表示沒有插頭,1表示有左端點(左括號),2表示有右端點(右括號),括號互相匹配。

首先如果當前格仔不能走那麼只能從沒有b1和b2的位置轉移過來:

if(!a[i][j])

}

然後對於當前格仔能走分多種情況討論:

!b1&&!b2

對於這種情況我們要加入兩個插頭(乙個左插頭乙個右插頭)來使這個點被走過。

else if(!b1&&!b2)
在**中就是加了第j條線的左括號和j+1的右括號。

!b1&&b2

只有乙個插頭,並且是向下的,於是可以選擇把這個插頭向下延伸或者向右:

else if(!b1&&b2)
如果能向右延伸就向右延伸,如果能向下延伸,先把第j+1條線的插頭去掉,改成第j條線的向下的插頭。

b1&&!b2

有橫著戳過來的插頭,可以繼續向右延伸或者改成向下。

else if(b1&&!b2)
另外上面兩種情況裡面\(\times b1/b2\)是因為不改變它們作為左括號還是右括號的性質。

b1=1&&b2=1

兩個左括號相遇合併了,我們把這兩個左括號刪掉,但為了保證合法,把右邊原來的乙個右括號改成左括號。

else if(b1==1&&b2==1)

}}

b1=2&&b2=2

和上面類似,找到左邊的左括號改成右括號。

else if(b1==2&&b2==2)

}}

b1=2&&b2=1

兩個左右括號可以直接刪掉了,雖然是背靠背的但是不影響,因為一定還有剩下的左右括號配對。

else if(b1==2&&b2==1)
b1=1&&b2=2

說明這可以當做終點了,如果這是終點那麼累加到答案裡面,否則棄掉。

else if(i==ex&&j==ey)ans+=num;
然後這幾個狀態大概就可以處理所有情況了,放個全碼:

#include#include#include#include#includenamespace emtwhile(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}

inline void file()

inline int max(int a,int b)inline int min(int a,int b)

}else if(!b1&&!b2)else if(!b1&&b2)else if(b1&&!b2)else if(b1==1&&b2==1)

}}else if(b1==2&&b2==2)

}}else if(b1==2&&b2==1)else if(i==ex&&j==ey)ans+=num;}}

}pi(ans);

return 0;

}}signed main()

刷題去了

學習筆記 插頭DP

基於連通性的狀壓dp問題。一般是給你乙個網格,有一些連通性的限制。鏈結題意 網格圖,去掉一些點,求哈密頓迴路方案數。一般按格遞推 從上到下,從左到右 每個格仔要從四個方向中選兩個作出邊。我們只需要記錄紅色的輪廓線的狀態,是否有邊伸出這個線 稱之為插頭 還要記錄伸出來的邊的連通性。記錄連通性的方法 最...

插頭DP學習

隊內沒人會插頭dp,感覺這個不會不行。所以我還是默默去學了一下,學了一天,感覺會了一點。對於每一行,一共有j 1個插頭,如果是多迴路類的題目,比較簡單,可以用1表示有插頭,0表示沒有插頭,這樣就可以愉快轉移了,對於當前出來的位置 i,j 與它有關的插頭有j 1和j 那麼我們可以列舉狀態經行轉移。對於...

插頭DP 入門

強烈推薦 hdu 1693 eat the trees 多迴路的不用判聯通狀態,二進位制即可,轉移情況2 2種。時間o n m 2 n 空間o n 2 n 插頭dp include include const int maxm 13 const int maxn 1 12 typedef long ...