1. 程式人生 > 其它 >組合數學之計數DP

組合數學之計數DP

------------恢復內容開始------------

計數類 $DP$ 在組合計數中是一類十分常用的演算法,下面筆者以一些例題作為講解,一來自己複習,二來可以幫助提升對計數題的思維與感受,就計數問題而言,多做這類題目才會找到感覺,其中會插入一些二項式反演的內容,也是和計數息息相關的。

這些題目,關鍵在於理解,而不在於實現的程式碼,所以筆者建議在做這一類題目時,不用去實現程式碼,理解思想即可,既能提升思維,又節約時間,這裡總結了一些比較好的計數題目。

做計數類問題,一般要考慮上一狀態與當前要計算的狀態之間的對應關係,當前的狀態必然是從上面的某狀態形成的局面加上一部分比較可計算的東西得來的。

\(DP\) 轉移方程一定要深刻理解,也要先嚐試自己推,不過可以先看前面的一些例題,再去自己思考其他題目。

CQOI2011 放棋子

題意

在一個 \(n\)\(m\) 列的棋盤裡放 \(c\) 種顏色的棋子,使得每個格子最多放一個棋子,且不同顏色的棋子不能在同一行或者同一列,有多少種方法?

如圖,左圖為合法,右圖為不合法。

Solution

首先這樣考慮,由於相同顏色的棋子會直接佔領某些行和某些列,導致這些行和列組成的那些位置不能再放入其他顏色的棋子,所以每種棋子擺放的方案是相對獨立的,會獨立地佔領一塊區域,而這些區域的劃分依據則是完整的行與完整的列。

那麼我們就設 \(f[i][j][k]\)

表示前 \(k\) 種顏色填入任意 \(i\)\(j\) 列之中(這類題都可以這麼設),也即它們佔領了 \(i\)\(j\) 列,但這些被佔領的完整行列可以是任意選取的。

考慮怎麼轉移,自然的想法是列舉當前的第 \(k\) 種顏色佔領多少行多少列,那肯定就需要考慮當前,於是又設 \(g[i][j][k]\) 表示剛好某種顏色的 \(k\) 枚棋子放入任意的 \(i\)\(j\) 列。

\(f[i][j][k]=\sum_{l=0}^{i-1} \sum_{r=0}^{j-1} f[l][r][k-1]*g[i-l][j-r][num[k]]*C_{n-l}^{i-l}*C_{m-r}^{j-r}\)

其中 \((i-l)(j-r) \ge num[k]\) ,原因是留出的空位一定要比那種顏色的棋子總數多,這樣才放得下。

這裡其實列舉的是前 \(k-1\) 種顏色放了多少行多少列,和上面提到的列舉當前是等價的,方案是對應的,那麼當前就剩下 \(i-l\) 行和 \(j-r\) 列了,又因為任意選,所以再乘上兩個組合數即可。

我認為比較容易出錯的是 \(l\) 要從 \([0,i-1]\)\(r\) 要從 \([0,j-1]\) ,想一下,第 \(k\) 種顏色必然佔據至少一行和一列,而放第一種顏色時,棋盤上沒有棋子,所以從 \(0\) 開始。

答案就是 \(\sum_{i=1}^n \sum_{j=1}^m f[i][j][c]\)

筆者認為這題比較難的是想出這個狀態,方程是比較好寫的。

[SCOI2005] 掃雷

題意

\(n*2\) 的棋盤裡面,給定一列掃雷的數字,求有多少種雷的分佈方式?

Solution

\(f[i][1/0][1/0]\) 表示考慮前 \(i\) 位的填數,當前這一位是否放,下一位是否放的方案數。

那麼每一位只有 \(1.2.3\) 三種可能的數字,分類討論即可。

如果填的 \(1\) 個數超過 \(a_i\) ,那這個狀態的 \(DP\) 陣列賦值 \(0\) 即可。

下面以 \(a_i=2\) 為例:

\(f[i][1][1]=f[i-1][0][1]\)

\(f[i][1][0]=f[i-1][1][1]\)

\(f[i][0][1]=f[i-1][0][1]\)

\(f[i][0][0]=0\)

打下草稿就會發現, \(i\) 階段的方案數唯一對應著一種上一階段 \(i-1\) 的方案數,正確性顯然,

這個題學到的一個 \(trick\) 就是,設計狀態時,如果只設計出包含當前和之前某狀態的話,不好進行轉移,那麼不妨把接下來的狀態加入方程。

例如本題,因為當前格子的數由三個位置決定,故需要考慮下一位,同時, \(i-1\) 某些狀態也直接對應一部分 \(i\) 狀態,加上即可。

[AHOI2009] 中國象棋

題意

在一個 \(n\)\(m\) 列的棋盤上,讓你放若干個炮(可以是 \(0\) 個),使得沒有一個炮可以攻擊到另一個炮,請問有多少种放置方法?

也就是說,每行每列最多兩個炮。

Solution

這題感覺和第一個例題挺像的。

考慮到最多兩個炮,於是在狀態裡把個數表示出來,設 \(f[i][j][k]\) 表示前 \(i\) 行,有 \(j\) 列一個炮,有 \(k\) 列兩個炮。

則有 \(m-j-k\) 列沒放,分別考慮當前行放置 \(0,1,2\) 個炮的情況,分類討論。

這樣的話,行的限制在轉移時列舉得以滿足,列的限制在狀態中滿足。

轉移方程如下:

當前行放 \(0\) 個棋子:
\(f[i][j][k]=f[i-1][j][k]\)

當前行放 \(1\) 個棋子:
\(f[i][j][k]=f[i-1][j-1][k]*(m-j-k+1)+f[i-1][j+1][k-1]*(j+1)\)

理解一下,其實就是考慮了這個炮放在之前沒有炮的地方還是有一個炮的地方。

當前行有 \(2\) 個棋子,方法也是類似,答案就是 \(\sum_{l=0}^m\sum_{r=0}^mf[n][l][r]\) ,其中 \(l+r \le m\)

當前狀態的炮的分佈,與上一狀態的炮的分佈十分相關,這題感覺也是狀態較為難設,方程比較好寫。