P6275 [USACO20OPEN]Sprinklers 2: Return of the Alfalfa P 題解
一道很妙的 DP 題,一眼過去感覺這好像是個輪廓線 DP,然後這道題確實是輪廓線 DP 但不是 \(2^n\) 那種的。
本篇題解參照了其他題解的思路 (我從來沒寫過這種套路的題),在此表示感謝。
首先規定一下本文中的 \((i,j)\) 不是指第 \(i\) 行第 \(j\) 列,而是指網格線的第 \(i\) 行第 \(j\) 列這個點,如下圖所示:
紅色點就是 \((4,5)\)。
這道題的輪廓線就是從 \((0,0)\) 到 \((n,n)\) 的一條往右下線。
設 \(f_{i,j,0/1}\) 表示前面 \(i\) 行都全部覆蓋,當前輪廓線終止在 \((i,j)\),輪廓線的最後一根方向是往右還是往下時的方案數。
首先看幾個結論:
- 輪廓線的拐點數就是必須要放的灑水器數。
比如說下圖,有 5 個拐點,那麼就要放 5 個灑水器。
- 假設上圖必須要放 \(k\) 個灑水器,整張圖能放灑水器的面積為 \(S\),那麼對於當前輪廓線的總方案數為 \(2^{S-k}\)。
這個結論還是很明顯的吧,就是因為剩下的點可放可不放,而且每個點能放的灑水器數量只有一種。
好的又設 \(sum_{i}\) 表示這一行的能放灑水器的數量(空地數),可以開始推方程了。
對於 \(f_{i,j,0}\):
我們發現其需要從 \(f_{i,j-1,0/1}\) 轉移。
- 從 \(f_{i,j-1,0}\) 轉移。
此時是這樣的:
發現拐點數沒變,覆蓋總面積也沒變,因此 \(f_{i,j-1,0}\) 的貢獻是 \(f_{i,j-1,0}\)。
- 從 \(f_{i,j-1,1}\) 轉移。
發現新增了一個紅色的灑水器,總面積沒變,對應的 \(2^{S-k}\) 變為 \(2^{S-k-1}\),因此 \(f_{i,j-1,1}\) 的貢獻是 \(\dfrac{f_{i,j-1,1}}{2}\)。
綜上,\(f_{i,j,0}=f_{i,j-1,0}+\dfrac{f_{i,j-1,1}}{2}\)。
對於 \(f_{i,j,1}\):
注意 \(f_{i,j,1}\) 需要從 \(f_{i-1,j,0/1}\) 轉移過來,因此面積數和拐點數都有變動。
\(f_{i-1,j,0}\) 的貢獻:面積數增加 \(sum_i\),拐點數增加 1,方案數變化為 \(2^{S-k} \to 2^{S-k+sum_i-1}\),因此貢獻是 \(f_{i-1,j,0} \times 2^{sum_i-1}\)。
\(f_{i-1,j,1}\) 的貢獻:面積數增加 \(sum_i\),拐點數不變,方案數變化為 \(2^{S-k} \to 2^{S-k+sum_i}\),因此貢獻是 \(f_{i-1,j,1} \times 2^{sum_i}\)。
所以 \(f_{i,j,1}=f_{i-1,j,0} \times 2^{sum_i-1}+f_{i-1,j,1} \times 2^{sum_i}\)。
這一組轉移可以參考下面兩幅圖:
注意 DP 的時候 \(i : \text\color{red}{0} \to n,j : \text\color{red}{0} \to n\),整體程式碼還是很好寫的。
Code:GitHub CodeBase-of-Plozia P6275 [USACO20OPEN]Sprinklers 2 Return of the Alfalfa P.cpp