1. 程式人生 > 其它 >反悔貪心學習筆記

反悔貪心學習筆記

\[\huge\rm 反悔貪心 \]
\[\Large\rm 演算法簡介 \]

\(\quad\)反悔貪心是對貪心策略的一種優化,有些時候貪心策略是錯的,但如果可以撤銷之前的操作,那麼就會變為正確的。

\(\quad\)反悔貪心主要有兩種。一種是反悔堆,即將之前沒有選擇的操作加入一個(類)堆中,保證每次取出的堆頂都是當前沒有選擇的策略中最優的,若由於當前操作,則將其替換。還有一種是反悔自動機,可以設計一個自動機來決策當前的操作。


\[\Large\rm 習題 \]

\(\large\rm [USACO09OPEN]Work~Scheduling~G\)

\(\quad\)反悔貪心板子題。

\(\quad\)按任務按截止時間排序,若當前這個工作做得了(即已經做的件數小於這個工作的截止日期),那麼直接做,並把它的價值放進優先佇列中。若當前這個工作沒時間做了,我們發現如果把前面一個工作去除,這個工作就做得了,於是對比小根堆頂的價值與他的價值,誰大就保留誰。

\(\quad\)時間複雜度 \(\Theta(n\log n).\)

\(\large\rm [JSOI2007]建築搶修\)

\(\quad\)反悔貪心板子題 \(\times 2.\)

\(\quad\)我們發現每個建築的貢獻都是一樣的。按建築的 \(T2\) 排序,一個個列舉,若當前建築可以修(即之前已經選的建築的 \(T1\) 之和加上當前這個建築的 \(T1\) 不大於當前建築的 \(T2\)),那麼就直接將這個建築的 \(T1\) 加上並放進優先佇列,並把答案 \(+1.\) 若不能修,考慮到價值一樣的情況下最小化總修理時間,那麼將大根堆的堆頂取出,若其大於當前點的 \(T1\),更新花費總時間。

\(\quad\)時間複雜度 \(\Theta(n\log n).\)

\(\large\rm [P2107]小Z的AK計劃\)

\(\quad\)反悔貪心板子題 \(\times 3.\)

\(\quad\)首先按機房位置升序考慮,假設你有一種到了機房就必須 \(\rm AK\) 的慾望(實際上是因為如果不 \(\rm AK\) 這個機房,就沒必要走到這裡,而是在之前停下),那麼如果當前的總用時加上 \(\rm AK\) 這個機房的用時不到 \(m\) 的時候,你就可以直接 \(\rm AK\),並將這個機房的 \(\rm AK\) 用時加入大根堆。若當前時間不夠,就不斷彈出堆頂,直到剩下的時間足夠 \(\rm AK.\)

一邊列舉一邊記錄當前最優答案即可。

\(\quad\)時間複雜度 \(\Theta(n\log n).\)

\(\large\rm [CF865D]Buy~Low~Sell~High\)

\(\quad\)首先考慮一個貪心,對每個數選擇它後面第一個比他大並且還沒被選擇的數,容易發現這樣是錯的,\(\rm Hack\) 資料如下 :

4
1 2 3 4

\(\quad\)於是我們考慮給上面那個貪心加上反悔操作,每次將當前值放入小根堆中,若當前列舉的這個數大於小根堆堆頂,那麼就將其賣掉,獲得 a[i]-Q.top() 的貢獻。同時,為了之後能夠反悔,我們還要將它也加入堆中,我們發現,如果後面還有一個數將其賣出,那麼其等價於直接將此時的堆頂賣出。這樣一來,如果我們認為某個點要賣出,那麼它就要入堆兩次。

\(\quad\)時間複雜度 \(\Theta(n\log n).\)

\(\large\rm [國家集訓隊]種樹\)

\(\quad\)考慮選了一個數以後會發生什麼,它兩邊的數都不能選了。於是考慮在選了一個點之後,把它兩邊的數標為不能選,然後在原處加入一個權值為 \(a_{i-1}+a_{i+1}-a_i\) 的點。每次取最大的點,若已標為不能選,就跳過,否則選他,然後將兩邊的點刪去,更新連結串列順序。

\(\quad\)容易發現,這樣做是對的,並且可以用優先佇列維護,時間複雜度 \(\Theta(n\log n).\)