感謝 $ \text{orzws/chy} $ 傾情授課。
貪心與動態規劃的一個使用條件是無決策後效性。這個我暫時不大理解,沒法寫總結
然後就是貪心的一些證明方式。聽說很多時候貪心是猜後證明的,然後我可以不證明 對拍試試正確性是麼.jpg
-1. 證明方式
-
微擾:見下 排序貪心;
-
反證法;
-
數學歸納法;
-
局部最優作用範圍擴展不會使得整體更劣;
-
決策包容性:作出局部最優決策後,問題狀態空間中的可達集合包含了作出其他任何決策後的可達集合。見下 反悔貪心。
0. 樸素貪心
AT2557 [ARC073C] Ball Coloring
所有球中標的最大與最小值一定會被選中,根據此分類討論。
欽定 \(max\) 取紅色,若:
-
\(min\) 取藍色。那麼剩下的貪心,小的給藍,大的給紅。
-
\(min\) 取紅色。那麼剩下的枚舉最小值然後二分最大值,經典求最小極差法。
P2587 [ZJOI2008]泡泡堂
大型田忌賽馬(?)
爺的做法:
暴力 \(set\) 日一下()
優先拿小的去日。
題解做法:
最好的情況是,優先用弱的去贏弱的,打不過就看最強的能不能打過最強的,不行就替補()
最壞的情況是,把兩支隊伍反過來就可以了。
1. 排序
既然是排序我們就需要關鍵字。
大致做法是這樣的。只考慮相鄰兩者交換的影響,列不等式求解,得到需要用來排序的關鍵字。
或者說假設一個最優解,將最優解的鄰項交換,看看答案會不會變劣。
(這個叫鄰項交換法,也叫微擾?)
AT2672 [AGC018C] Coins
既然有三個選項,不妨縮成兩個()
假設全都選了金幣(\(A\))
設 \(f_i=B_i-A_i\),\(g_i=C_i-A_i\)
現在就要選 \(y\) 個人選 \(f\) , \(g\) 個人選 \(g\) 。
考慮 \(i\) ,\(j\) 兩者, \(i\) 選 \(f\) , \(j\) 選 \(g\) 比 \(i\) 選了 \(g\) , \(j\) 選了 \(f\) 更優,則:
即:
所以按 \(f-g\) 排個序就好了。
P2123 皇后遊戲
忘了先講國王遊戲遼。
皇后遊戲中,大臣獲得的獎金數目是遞增的,我們要使得\(c_n\)儘可能小。
影響\(c_i\)的只有\(c_{i-1}\),前\(i-1\)個數的順序並不產生額外影響。前綴子問題最優可推到全局最優。
考慮鄰項\(i\),\(j\)交換。設\(i\),\(j\)的前一個獲得獎金爲\(K\),他們前面所有人的\(a\)值和爲\(S\),則:
- 當\(i\)在\(j\)之前時:
- 當\(j\)在\(i\)之前時:
當\(i\)在前比\(j\)在前更優:
\(max\{K+b_i+b_j,S+a_i+b_i+b_j,S+a_i+a_j+b_j\}<max\{K+b_i+b_j,S+a_j+b_i+b_j,S+a_i+b_i+a_j\}\)
\(k+b_i+b_j\)那項可以直接去掉了。對於剩下的項,取負再加上\(S+a_i+b_i+a_j+b_j\),得到:
經過一系列我看不下去的證明,這個比較是有傳遞性的。
聽說直接把這個比較扔進cmp
函數也能過。我感覺會\(RE\)(?)
嚴格弱序
std::sort
中比較符必須有的性質,具體如下:
-
非自反性(自己跟自己比較爲false)
-
非對稱性(若x與y比較爲true,則y與x比較爲false)
-
傳遞性(若x與y比較爲true,y與z比較爲true,則x與z比較爲true)
-
不可比性的傳遞性(若x與y互相比較爲false,y與z互相比較爲false,則x與z互相比較爲false)
對,他是沒有不可比性的傳遞性的。
優化的話,由於\(a\)的前綴和可能影響全局答案,優先把\(a\)較小的放前面。
證不動正確性了。;w;
P1248 加工生產調度
2. 數據結構維護相關信息
3. 反悔貪心
貪心的時候,需要注意的是,一定只考慮相鄰一步的操作。一些題目確實需要把看起來多樣繁瑣的操作拆成單位簡單操作疊加作用的結果。
仔細思考了一下反悔貪心的原理。一般來說需要一個優先隊列,來存儲接着某項直接操作/反悔操作的增益
這種貢獻是逐步累加的,直到大根堆堆頭 $ \leq 0 $ 。
重新百度了一下((),反悔貪心大致是分爲反悔堆
和反悔自動機
。
反悔堆
通過堆來維護當前貪心策略最優解。
USACO09OPEN 工作調度Work Scheduling
有 $ n $ 項工作,每 $ i $ 項工作有一個截止時間 $ D_i $ ,完成每項工作可以得到利潤 $ P_i $ ,求最大可以得到多少利潤。
比較基礎。按照 $ D_i $ 排序,能做的工作就做,然後以 $ P_i $ 作爲關鍵字放入小根堆。遇到不能做的工作,取出堆頂,如果 $ P_i>top $ 就換掉。
CF436E Cardboard Box
反悔堆的一個常見做法。把可行的各種下一步的策略分別放到不同的堆裏面。
比如這道題,從 \(i\) 個星星到 \(i+1\) 個星星就有如下取法:
-
從未取星星的一關 \(i\) ,獲取一個星星,代價 \(a_i\);
-
從取了一顆星星的一關 \(i\),再獲取一個星星,代價 \(b_i-a_i\);
-
從取了一個星星的一關 \(i\),把星星扔回去,取另一個未取星星一關 \(j\) 的兩個星星,代價 \(b_j-a_i\);
-
從取了兩個星星的一關 \(i\),把星星扔回去一個,取另一個未取星星一關 \(j\) 的兩個星星,代價 \(b_j-(b_i-a_i)\)。
每次取代價最小一個就行了。
具體的實現細節上,維護的是:
-
未取星星關的 \(a_i\)(操作1);
-
未取星星關的 \(b_i\)(操作3,4)
-
取一星星關的\(b_i-a_i\)(操作2);
-
取一星星關的\(-a_i\)(操作3);
-
取兩星星關的\(a_i-b_i\)(操作4)。
要把堆中不符合 \(type\) 的數值 \(pop\) 掉。可以看一下這位的實現,寫的不錯(?)
AT2672 [AGC018C] Coins
重新再看一下這個題。
假設先任意選擇。對於三個不同選擇的人 \(i\) \(j\) \(k\),反悔的操作可能有:
-
\(i \longleftrightarrow j\) (\(B_i-A_i+A_j-B_j\))
-
\(j \longleftrightarrow k\) (\(C_j-B_j+B_k-C_k\))
-
\(i \longleftrightarrow k\) (\(C_i-A_i+A_k-C_k\))
-
\(i \longrightarrow j \longrightarrow k \longrightarrow i\) (\(B_i-A_i+C_j-B_j+A_k-C_k\))
-
\(i \longrightarrow k \longrightarrow j \longrightarrow i\) (\(C_i-A_i+B_k-C_k+A_j-B_j\))
具體的維護:
-
選了 \(A\) 的 \(B_i-A_i\),\(C_i-A_i\)
-
選了 \(B\) 的 \(A_i-B_i\),\(C_i-B_i\)
-
選了 \(C\) 的 \(A_i-C_i\),\(B_i-C_i\)
反悔自動機
一般是過程中累積貢獻,通過差值消去中間項(反悔)來快速得到全局最優解。
CF865D Buy Low Sell High
基本的貪心思路是:買入價格最小的股票,在能賺錢的時候賣出。
當然這個是錯的()
之前也寫過一個類似的題目(紀念品/股票市場),由於一天之內價格不會變動,所以可以假裝我們先賣了,然後再買進來,之後再賣掉。對於 $ i \leq j \leq k $ ,具體的表現形式是:
$ (p_k-p_j)+(p_j-p_i)=p_k-p_i $
先買入 $ i $ ,再在 $ j $ 賣出,獲得 $ p_j-p_i $ 的貢獻。然後對於 $ j $ ,接下來有兩步可能的操作:
-
在 $ p_j $ 又買回來,再往後考慮。
-
在 $ p_j $ 又買回來(相當於在 $ j $ 啥都沒幹)後,再買 $ p_j $ 。
於是我們可以在隊列裏面塞兩遍 $ p_j $ 。
-
如果貢獻加了一次 $ p_k-p_j $ ,表示 $ j $ 反悔,在 $ i $ 買在 $ k $ 賣;
-
如果貢獻加了一次 $ p_{k_1}-p_j $ ,再加一次 $ p_{k_2}-p_j $ ,表示 $ j $ 先反悔,再 $ j $ 買 $ k_2 $ 賣。
P1792 [國家集訓隊]種樹
反悔自動機,做法是把所有選項扔進堆裏,取出堆頭 $ b $ ,把與 $ b $ 相鄰的兩項 \(a\),\(c\) 合成一個新點 \(a+c-b\),再扔回堆裏面。
看完題解還是糾結了半天兩個問題:
-
這個反悔存儲方式是否覆蓋了所有可能的下一步
-
或者未存儲的反悔策略不可能成爲最優的答案
淺列了一下情況。