1. 程式人生 > 其它 >問題 C: 規劃-題解

問題 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\)

個點,肯定是 \(\frac{n+1}{2}\) 個點取滿,其他點不取。這樣一定是最優的。具體來說,隨便舉個例子,比如兩點相隔 \(6\) ,那麼兩個點各佔 \(3\) 獲得的面積 \(=2*get(3)=2*2*3^2=36\)。然而如果一個點佔 \(6\),那麼面積 \(=get(6)=72\),明顯更優。與十分做法相結合即可獲得 \(20\) 分。

五十分做法

我們考慮前兩個做法優化。對於十分做法,首先有一個顯然的優化:字首和優化。這個隨便搞一搞就好了。當然正解可以不用這個優化。

接著我們發現這個與值域有關的方程非常麻煩,我們考慮優化。我們發現其實有很多狀態是無用的,相當於這些狀態轉移了也不會對下一個狀態和答案做出任何貢獻,因此我們考慮剪枝這些無用狀態。相當於對於每一個點,找到一個大小盡量小的集合 \(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\)

因此,我們會發現這樣有用的狀態會是一段 <<<<<<<>,其中左邊的一個狀態未定,所以可以是 ><<<<<<<<>。如果把整個序列反過來,我們發現 <>>>>>>>>>>< 也將是有用狀態。

證畢!

那麼這道題就圓滿結束。