問題 C: 規劃-題解
C: 規劃
在此我們約定,以一個點往外擴充 \(j\) 格表示同時往左右移動 \(j\) 格,佔用 \(2\times j\) 的格子。舉個例子,擴充 \(3\) 格的面積是 \(2\times 3^2=18\),具體看第一個樣例的第二個圖形。
\(l_i\) 表示 \(a_i-a_{i-1}\),\(r_i\) 表示 \(a_{i+1}-a_{i}\)。我們可以得出,對於 \(2\le i\le n\), \(l_i=r_{i-1}\)
\(get(x)\) 表示擴充 \(x\) 格獲得的面積。
選滿表示當前點頂到兩邊兩個點的其中一個。
我們可以把一個點擴充後的菱形抽象成一個圓,擴充格子就是圓的半徑。這樣或許更好理解。
十分做法
首先有一個簡單的動態規劃:設 \(f_{i,j}\) 表示前 \(i\) 個點,往外擴充 \(j\) 格,能夠獲得的最大面積。對於點 \(i\) 的上限,就是 \(\min(l_i,r_i)\) 。轉移的話,暴力就是對於前面一個點,所有滿足 \(j+k\le l_i\),\(f_{i,j}=\max{f_{i-1,k}}+get(j)\) 。也就是說,當前面積加上上一個點的 DP值。當然,如果狀態不存在,就設為零就好了。這樣可以獲得 \(10\) 分的好成績。
二十分做法
對於第 \(4\) 個子任務,可以推出如果有兩個點,兩個點各佔一半肯定比一個點佔完更優。因此可以得出結論:這 \(n\)
五十分做法
我們考慮前兩個做法優化。對於十分做法,首先有一個顯然的優化:字首和優化。這個隨便搞一搞就好了。當然正解可以不用這個優化。
接著我們發現這個與值域有關的方程非常麻煩,我們考慮優化。我們發現其實有很多狀態是無用的,相當於這些狀態轉移了也不會對下一個狀態和答案做出任何貢獻,因此我們考慮剪枝這些無用狀態。相當於對於每一個點,找到一個大小盡量小的集合 \(S_i\)
因此對於每一個點,我們考慮選滿這個點之後會對周圍的點造成的影響,相當於選滿這個點周圍的點就緊挨著這個點,直到無法緊挨著,相當於換了一個連通塊。我們把這些點的這些值全部放到集合 \(S_i\) 中,同時考慮一個點肯定還有不放、放滿兩種狀態要考慮。
轉移時就只用考慮這些狀態就可以了。因為值域較大,我使用的是 \(map\) DP,字首和優化,時間複雜度最壞情況 \(O(n^2\log_2(n))\)。考慮相結合二十分做法即可獲得五十分。這裡是為 AC 做法鋪墊。
因此我在考場上使用這個做法獲得了 \(40\) 分,剩下 \(10\) 分是因為INF設小了!!痛失10分
AC做法
其實 AC 做法就是五十分做法的優化。我們考慮其實對於集合 \(S_i\) ,還是有很多狀態沒有使用。那我們接著考慮剪枝。怎麼還剪啊
對於每一個點,有兩種情況必須考慮:當前點不選和當前點選滿。接下來就是高危警告
先放結論。
對於每一個點,我們定義如果這個點的 \(l_i\le r_i\) ,那麼我們用 \(<\) 號表示這個點,否則就用 \(>\) 號表示這個點。那麼這個點集就可以表示為小於大於號組成的序列,比如 \(<>><<>><<><><<>>\) 。
那麼接下來,我們先考慮**中間沒有 > **的兩個 \(>\) 。因此,中間肯定是一堆 \(<\) 。那麼我們直接把最右邊的 \(>\) 選滿,中間的所有點就看看能不能跟右邊的圓相切,能就放進當前點的集合。形象的,相當於對於一個 \(><<<<<<<<<>\) 的子區間,我們最右邊的點選滿,其他點嘗試在右邊點選滿的基礎上選滿。
對於 \(<>>>>>>>>><\) 的子區間我們也類似的操作。
於是我們發現,最後每個集合的點最多隻有 \(4\) 個,而這恰好包括了最優解。完結撒花 好吧,還沒有完。對於每個點,我們發現集合恰好包括了最優解的所有可能。為什麼呢?我也不知道
當然,只要知道這個結論你就可以做題了。具體操作就是要不然就是 \(map\) DP,要不然就直接 \(vector\) 暴力 DP 。事實證明兩種做法都能 AC,但是我用了火車頭。。注意時空優化!!!
下面是證明部分,由另一人執筆。
為了證明這個貪心,我們先做億點點準備工作(
設 \(r_i\) 表示第 \(i\) 個圓的半徑, \(p_i\) 表示第 \(i\) 個圓的圓心橫座標(即題目中的 \(x_i\))。
設 \(p_0=-\inf,p_{n+1}=\inf\)
結論一:
對於每一個點,\(r_i=0\) 或 \(r_i=\min(p_i-p_{i-1},p_{i+1}-p_i)\) 在考慮範圍內
顯然。
結論二:
在最優情況中,每一個圓至少和旁邊一個圓相切。
顯然,如果不和周圍圓相切,我們可以把 \(r_i\) 調大,而仍然合法。
結論三:
當一個圓和旁邊一個圓相切,一個圓相離時(假設相切的圓是第 \(i+1\) 個)滿足 \(r_i<r_{i+1}\)。
考慮反證法,如果 \(r_i\ge r_{i+1}\),我們考慮把圓 \(i\) 調大 \(t\),並把圓 \(i+1\) 調小 \(t\),此時一定存在一個 \(t\) 使得調整後仍然合法。
考慮調整前後兩個方案中這兩個圓的貢獻的差:
\(\Delta S=(r_i+t)^2+(r_{i+1}-t)^2-r_i^2-r_{i+1}^2=2t_6+2t(r_i-r_{i+1})>0\),
所以調整後一定更優,所以此時一定滿足 \(r_i<r_{i+1}\)。
結論四:
當一個圓和旁邊兩個圓都相切時,滿足 \(r_i<\max(r_{i-1},r_{i+1})\)。
考慮和結論三一樣的證法,
不妨假設 \(r_{i-1}\le r_{i+1}\),
假設 \(r_i\ge r_{i+1}\),我們考慮把圓 \(i\) 調大使得經過 \(p_{i-1}\),則 \(r_{i-1}\) 會變成 \(0\),\(r_{i+1}\) 變成 \(r_{i+1}-r_{i-1}\),
\(\Delta S=(r_{i+1}-r_{i-1})^2+(r_i+r_{i-1})^2-r_{i-1}^2-r_i^2-r_{i+1}^2=r_{i-1}^2+2r_{i-1}(r_i-r_{i-1})>0\)
所以調整後一定更優,所以此時一定滿足 \(r_i<\max(r_{i-1},r_{i+1})\)。
接下來,我們考慮什麼樣的 \(r_i\in S_i\)。
- 對於 \(i=1/n\),取 \(0\) 或取頂滿的情況,
- 對於 \(i\in (1,n)\),假設 \(r_{i-1}\le r_{i+1}\),我們可以得到 \(r_i<r_{i+1}\)。
證明:如果圓 \(i\) 之和右邊相切,那麼 \(r_i<r_{i+1}\)(結論三),如果圓 \(i\) 和兩邊都相切,那麼也有 \(r_i<r_{i+1}\)(結論四),如果圓 \(i\) 和左邊相切,那麼 \(r_i<r_{i-1}<r_{i+1}\)。
所以,對於所有情況,都有 \(r_i<r_{i+1}\)。
接下來,我們考慮圓 \(i+1\) 右邊界是否經過 \(p_{i+2}\),如果經過,那麼圓 \(i+1\) 是 >
的,並且加入有用狀態;如果沒經過,我們會和剛才一樣,得到 \(r_{i+1}<r_{i+2}\),並且此時會發現 \(p_{i+1}-p_i=r_i+r_{i-1}<r_i+r_{i+1}=p_{i+2}-p_{i+1}\),因此圓 \(i+1\) 是 <
的,並繼續考慮圓 \(i+3\)。
因此,我們會發現這樣有用的狀態會是一段 <<<<<<<>
,其中左邊的一個狀態未定,所以可以是 ><<<<<<<<>
。如果把整個序列反過來,我們發現 <>>>>>>>>>><
也將是有用狀態。
證畢!
那麼這道題就圓滿結束。