1. 程式人生 > 其它 >P6275 [USACO20OPEN]Sprinklers 2: Return of the Alfalfa P 題解

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)\),輪廓線的最後一根方向是往右還是往下時的方案數。


首先看幾個結論:

  1. 輪廓線的拐點數就是必須要放的灑水器數。

比如說下圖,有 5 個拐點,那麼就要放 5 個灑水器。

  1. 假設上圖必須要放 \(k\) 個灑水器,整張圖能放灑水器的面積為 \(S\),那麼對於當前輪廓線的總方案數為 \(2^{S-k}\)
    這個結論還是很明顯的吧,就是因為剩下的點可放可不放,而且每個點能放的灑水器數量只有一種。

好的又設 \(sum_{i}\) 表示這一行的能放灑水器的數量(空地數),可以開始推方程了。

對於 \(f_{i,j,0}\)

我們發現其需要從 \(f_{i,j-1,0/1}\) 轉移。

  1. \(f_{i,j-1,0}\) 轉移。

此時是這樣的:


發現拐點數沒變,覆蓋總面積也沒變,因此 \(f_{i,j-1,0}\) 的貢獻是 \(f_{i,j-1,0}\)

  1. \(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