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