Greedy & DP
14th Sep. 2020
Greedy
什麼是Greedy?
什麼是Greedy?
短視近利
Dynamic Programming
什麼是Dynamic Programming?
什麼是Dynamic Programming?
分割之前的問題,填表
End
來看《君の名は。》吧!

何謂Greedy?
什麼是Greedy?
短視近利
每次都走目前最好的
會議排程問題
有\(N\)場會議,第\(i\)個會議起訖時間都是從\(s_i\)到\(t_i\)的。然而,只有一個會議室,且每一個會要不然不開,要不然完全開完。請問最多可以安排幾次兩兩不相撞的會議呢?
(\(N \leq 2\times 10^5, l_i, r_i \leq 10^9\))
想想看
怎麼樣拿才會最好?
想想看
怎麼樣拿才會最好?
- 排序?
- 照開始排序?
想想看
怎麼樣拿才會最好?
- 排序?
- 照開始排序?
- 照長短排序?
想想看
怎麼樣拿才會最好?
- 排序?
- 照開始排序?
- 照長短排序?
- 照結束排序!
想想看
若每次都選可以選的中,結束時間最小的,則這樣子的答案一定不會比任何其他選擇差。
工作排程問題
你有一個機器和\(N\)個訂單,第\(i\)個訂單若完成,會得到\(w_i\)的錢,但是必須在\(d_i\)的時間前完成。若每一個工作要做的時間都是一時間單位,請問最大的獲益為何?
(\(N \leq 2 \times 10^5, d_i, w_i \leq 10^9\))
工作排程問題
- 對於每一個時間\(t\):
- 將截止時間為\(t\)的全部加入一個多重集合\(S\)中
- 從\(S\)中選價格最大的,在時間\(t\)就做這個工作了。
- 這個\(S\)留著,待著之後繼續考慮。
工作排程問題
Why?????????
如果看截止日期最大的那一個,那就直接放呀(如果有多個不就選最大的那個嗎)
然後剩下的再和截止日次大的哪些一起考慮⋯⋯
習題 ><
CF 1368B
Codeforces Subsequences
給定一個數字\(K\),請找一個最短的字串使得"codeforces"這個出現為那個字串的子序列至少\(K\)次。
(\(K \leq 10^{16}\))
CF 1396A
Multiples of Length
你有一個長度為\(N\)的序列\(a_i\)。你的目標是想要讓\(a_i\)都等於\(0\)。你可以做最多三次這個運算:
- 選擇任意的\(1 \leq l, r \leq N\)然後對於每一個\(a_i, i \in [l, r]\),都加上\(r - l + 1\)的某個倍數(數字可以不同,但都要是\(r - l + 1\)的倍數)
可以證明一定做得到,\(N \leq 10^5, |a_i| \leq 10^9\)
CF 1253D
Harmonious Graph
有一個\(N\)個點,\(M\)個邊的無向圖。一個圖稱為「和諧的」若且唯若對於所有的\(1 \leq l < m < r \leq N\),若點\(l\)能夠走到點\(r\),則點\(l\)就可以走到點\(m\)。請問我最少要加多少條邊才能讓圖變成和諧的呢?
(\(N, M \leq 2 \times 10^5\))
CF 1197C
Array Splitting
給你一個長度為\(N\)的非嚴格遞增自序列\(a_i\),請將序列分為\(K\)個連續的區塊,使得
\(\sum_i^K \max(i) - \min(i)\)
最小。\(\max(i), \min(i)\)分別代表第\(i\)個區塊中的最大值和最小值。
何謂DP?
何謂DP?
Dynamic programming is both a mathematical optimization method and a computer programming method. The method was developed by Richard Bellman in the 1950s and has found applications in numerous fields, from aerospace engineering to economics."
--Wikipedia
1950年代由Richard Bellman提出
老闆喜歡的名稱(?)
何謂DP?
將問題分割為很小的子問題,(通常是)利用填表的方式將答案求出。
斐波那契數列
斐波那契數列的定義如下:
-
\(a_0 = a_1 = 1\)
-
\(a_n = a_{n - 1} + a_{n - 2}\)
給你\(n\),請求\(a_n \pmod {10^9 + 7}\)?
斐波那契數列
斐波那契數列的定義如下:
-
\(a_0 = a_1 = 1\)
-
\(a_n = a_{n - 1} + a_{n - 2}\)
給你\(n\),請求\(a_n \pmod {10^9 + 7}\)?
int f(int n){
if(n <= 1) return 1;
return f(n - 1) + f(n - 2)
}
naïve 做法:\(O(\varphi^n)\),超爛
斐波那契數列
將之前的東西存起來!
const int M = 1e9 + 7;
int fib[1e5];
fib[0] = fib[1] = 1;
for(int i = 2; i < N; i++)
fib[i] = (fib[i - 1] + fib[i - 2]) % M;
\(O(n)\),快多了!
其實可以做到\(O(\log N)\),不過不是今天上課的範疇
背包問題
你有\(N\)個東西,第\(i\)個東西有價值\(v_i\)和重量\(w_i\)且只有一個。但是,你的包包最多只能承受最多為\(W\)的重量。請問你的包包在重量限制內,最多可以攜帶多少的價值?
\(O(NW)\)背包問題
若\(\text{dp}[i][j]\)代表「若只用前\(i\)個東西,且重量已經最多用了\(j\),這樣拿到的價值最多可以為何」,則可以列出式子
\(\text{dp}[i][j] = \max(\text{dp}[i - 1][j], \text{dp}[i - 1][j - w_i] + v_i)\)
不拿第\(i\)個
拿第\(i\)個
轉移順序
for(int i = 0; i < N; i++){
for(int j = W - 1; j >= 0; j--){
dp[i][j] = max(dp[i - 1][j], dp[i][j - w[i]] + v[i]);
}
}
注意,\(j\)要倒著轉移!想想看,如果不是如此,會出現什麼問題呢?
無限背包
每個物品都可以任意拿
for(int i = 0; i < N; i++){
for(int j = 0; j < W; j++){
dp[i][j] = max(dp[i - 1][j], dp[i][j - w[i]] + v[i]);
}
}
有限背包
第\(i\)個物品可以拿\(c_i\)個
\(O(NW\log C)\)的解(二次方分拆):
- 將\(c_i\)個物品分為\(1\)個、\(2\)個、\(4\)個、\(8\)個⋯⋯\(c_i - (2^k - 1)\)個一組的,直接去做正常背包。
對於背包問題有興趣的人可以去查《背包九講》哦!
最長共同子序列(LCS)問題
給你兩個長度為\(N, M\)的序列\(A, B\),請找出一個字串\(S\)使得\(S\)是\(A\)的子序列也是\(B\)的子序列,且\(|S|\)最大。
(\(N, M \leq 10^3\))
\(X\)為\(Y\)的子序列若且唯若\(X\)可以透過\(Y\)刪減一些字元而其餘字元不改變其相對位置而得。
最長共同子序列(LCS)問題
令\(\text{dp}[i][j]\)代表\(A\)的前\(i\)個字元和\(B\)的前\(j\)個字元所構成的最長共同子序列。則
\(\text{dp}[i][j] = \begin{cases}\max(dp[i - 1][j - 1] + 1,dp[i - 1][j], dp[i ][j - 1])&\;, A_i = B_j\\\max(dp[i - 1][j], dp[i ][j - 1])&\;, \text{else}\end{cases}\)
\(O(NM)\)
最長遞增子序列(LIS)問題
給定一個長度為\(N\)的序列\(a_i\),請求一個\(a\)的子序列,使得其非嚴格遞增且其長度最長。
(Subtask 1: \(N \leq 10^3\)、Subtask 2: \(N \leq 10^5\))
最長遞增子序列(LIS)問題
\(\text{dp}[i]\)為「結尾在\(i\)的地方的最長遞增子序列可以多長」
\(\text{dp}[i] = \max_{j < i \text{且} a_j < a_i}(\text{dp}[j] + 1)\)
\(O(N^2)\), Subtask 1 AC
最長遞增子序列(LIS)問題
之後就會學到可以用線段樹之類的東西優化,但是這裏教一個比較酷的方法:每次都lower_bound,就可以了!
\(O(N \log N)\), Subtask 2 AC
int arr[N];
vector<int> v;
v.resize(N + 1);
fill(v.begin(), v.end(), INF);
for(int i = 0; i < N; i++){
*lower_bound(v.begin(), v.end(), arr[i]) = arr[i];
}
return lower_bound(v.begin(), v.end(), INF) - v.begin();
最長遞增子序列(LIS)問題
之後就會學到可以用線段樹之類的東西優化,但是這裏教一個比較酷的方法:
\(O(N^2)\), Subtask 1 AC
習題牛排!
最大連續和問題
給你一個長度為\(N\)的序列\(a_i\),請找到一組\(l \leq r\)使得\(\sum_l^r a_i\)最大。
(\(N \leq 10^5, |a_i| \leq 10^9\))
CF 1398D
Colored Rectangles
有三種木棒對,塗成三種顏色,分別有\(R, G, B\),分別為\(r_1, r_2, \dots, r_R\)、\(g_1, g_2, \dots, g_G\)、\(b_1, b_1, \dots, b_B\)。現在,你可以重複做這個操作:
- 將兩個不同顏色的木棒對組起來變成一個矩形,分數加上那個矩形的面積。此兩個木棒將不能再使用。
請問最大的總分數為何?
(\(N \leq 200, r_i, g_i, b_i \leq 2000\))
Greedy & DP
By sean5463
Greedy & DP
- 179