數字dp是一種計數用的dp,一般就是要統計乙個區間[le,ri]內滿足一些條件數的個數。所謂數字dp,字面意思就是在數字上進行dp咯。數字還算是比較好聽的名字,數字的含義:乙個數有個位、十位、百位、千位…數的每一位就是數字啦!
之所以要引入數字的概念完全就是為了dp。數字dp的實質就是換一種暴力列舉的方式,使得新的列舉方式滿足dp的性質,然後記憶化就可以了。
兩種不同的列舉:對於乙個求區間[le,ri]滿足條件數的個數,最簡單的暴力如下:
for
(int i=le;i<=ri;i++)if
(right
(i)) ans++
;
新的列舉:控制上界列舉,從最高位開始往下列舉,例如:ri=213,那麼我們從百位開始列舉:百位可能的情況有0,1,2(覺得這裡列舉0有問題的繼續看)
然後每一位列舉都不能讓列舉的這個數超過上界213(下界就是0或者1,這個次要),當百位列舉了1,那麼十位列舉就是從0到9,因為百位1已經比上界2小了,後面數字列舉什麼都不可能超過上界。所以問題就在於:當高位列舉剛好達到上界是,那麼緊接著的一位列舉就有上界限制了。具體的這裡如果百位列舉了2,那麼十位的列舉情況就是0到1,如果前兩位列舉了21,最後一位之是0到3(這一點正好對於**模板裡的乙個變數limit 專門用來判斷列舉範圍)。最後乙個問題:最高位列舉0:百位列舉0,相當於此時我列舉的這個數最多是兩位數,如果十位繼續列舉0,那麼我列舉的就是以為數咯,因為我們要列舉的是小於等於ri的所以數,當然不能少了位數比ri小的咯!(這樣列舉是為了無遺漏的列舉,不過可能會帶來乙個問題,就是前導零的問題,模板裡用lead變數表示,不過這個不是每個題目都是會有影響的,可能前導零不會影響我們計數,具體要看題目)
由於這種新的列舉只控制了上界所以我們的main函式總是這樣:
int
main()
統計[1,ri]數量和[1,le-1],然後相減就是區間[le,ri]的數量了,這裡我寫的下界是1,其實0也行,反正相減後就沒了,注意題目中le的範圍都是大於等於1的
typedef
long
long ll;
int a[20]
;ll dp[20]
[state]
;//不同題目狀態不同
ll dfs
(int pos,
/*state變數*/
,bool lead/*前導零*/
,bool limit/*數字上界變數*/
)//不是每個題都要判斷前導零
//計算完,記錄狀態if(
!limit &&
!lead) dp[pos]
[state]
=ans;
/*這裡對應上面的記憶化,在一定條件下時記錄,保證一致性,當然如果約束條件不需要考慮lead,這裡就是lead就完全不用考慮了*/
return ans;
}ll solve
(ll x)
return
dfs(pos-
1/*從最高位開始列舉*/
,/*一系列狀態 */
,true
,true);
//剛開始最高位都是有限制並且有前導零的,顯然比最高位還要高的一位視為0嘛
}int
main()
}
鏈結 位運算作者(邦的軒轅)
運算符號 運算法則 與 兩個 位都為1時值才為1 或 兩個 位只要有乙個數為1,值就為1 非 取反,0變成1,1變成0 異或 兩個位相同為0,相異為1 記住一點,位運算是將十進位制的數轉化為二進位制後進行的運算,就像十進位制的加減乘除一樣,不過二進位制下更多的是加1,減1,乘以2,除以2,因為是二進...
P3694 邦邦的大合唱站隊 狀壓dp
n nn個人,有m mm個隊伍,每個人都屬於乙個隊伍。要求叫出一些人來,然後任意插入出來的空隙中使得同一隊的人在一起。求最少出列人數。如果知道最終的佇列就可以十分容易的計算答案了。考慮乙個乙個隊伍的放入最終序列中,因為m mm十分的小,所以我們可以狀壓表示排好了的隊伍集合,然後用乙個字首和統計在乙個...
P3694 邦邦的大合唱站隊 狀壓DP
bang dream 裡的所有偶像樂隊要一起大合唱,不過在排隊上出了一些問題。n個偶像排成一列,他們來自m個不同的樂隊。每個團隊至少有乙個偶像。現在要求重新安排佇列,使來自同一樂隊的偶像連續的站在一起。重新安排的辦法是,讓若干偶像出列 剩下的偶像不動 然後讓出列的偶像乙個個歸隊到原來的空位,歸隊的位...