動態規劃

區間動態規劃、狀態壓縮DP

動態規劃

  • 最佳子結構

    • 問題本身的最佳解可以從子問題的最佳解求得
  • 重複子問題

    • 相同的子問題重複出現

動態規劃

ex. 背包問題的最佳子結構

dp[W]=max_{i=1}^k\{dp[W-w_i]+v_i\}
dp[W]=maxi=1k{dp[Wwi]+vi}dp[W]=max_{i=1}^k\{dp[W-w_i]+v_i\}

背包重量

最高價值

假設有三個物品(k=3), 重量與價值分別為

w=\{1,2,5\}\ \ v=\{1,4,3\}
w={1,2,5}  v={1,4,3}w=\{1,2,5\}\ \ v=\{1,4,3\}
W=6
W=6W=6
4
44
1
11

1

8

9

dp[6]=max\{9+1, 8+4, 1+3\}=12
dp[6]=max{9+1,8+4,1+3}=12dp[6]=max\{9+1, 8+4, 1+3\}=12
dp[W]
dp[W]dp[W]

5

4

5
55

0

動態規劃

ex. 背包問題的重複子問題

背包重量

最高價值

W=5
W=5W=5
4
44

1

8

dp[W]
dp[W]dp[W]

5

4

3
33

背包重量

最高價值

w=\{1,2,5\}\ \ v=\{1,4,3\}
w={1,2,5}  v={1,4,3}w=\{1,2,5\}\ \ v=\{1,4,3\}
W=6
W=6W=6
4
44
1
11

1

8

9

dp[W]
dp[W]dp[W]

5

4

5
55

0

0

0
00

區間動態規劃

  • 顧名思義,dp陣列是儲存某個區間的最佳解

  • 最佳子結構重複子問題

L

R

L

R

i

i+1

dp[L][R] = max_{i=L}^{R-1}\{dp[L][i] + dp[i+1][R]+w(L,i,R)\}
dp[L][R]=maxi=LR1{dp[L][i]+dp[i+1][R]+w(L,i,R)}dp[L][R] = max_{i=L}^{R-1}\{dp[L][i] + dp[i+1][R]+w(L,i,R)\}

區間動態規劃 zerojudge - d652

題目:

給長度為n(<50)的正整數數列,每次操作可以合併相鄰的3個數,合併後中間的數被刪除,付出代價為三數相乘。

例如:相鄰三數為A, B, C則合併後剩下A, C操作代價為三數相乘AxBxC。

求合併到只剩2個數,操作過程代價總和最少為何

 

範例輸入:

1 2 3 4 5

 

範例輸出:

38

1 2 3 4 5

0

1 3 4 5

6

1 4 5

18

1 5

38

區間動態規劃 zerojudge - d652

dp[L][R] = max_{i=L}^{R-1}\{dp[L][i] + dp[i+1][R]+w(L,i,R)\}
dp[L][R]=maxi=LR1{dp[L][i]+dp[i+1][R]+w(L,i,R)}dp[L][R] = max_{i=L}^{R-1}\{dp[L][i] + dp[i+1][R]+w(L,i,R)\}
  1. 觀察操作有什麼特性

  2. 定義dp陣列每個維度index的意義

  3. 根據定義的意義嘗試找出轉移方法

  4. 找不出轉移方法->從第2步開始

區間動態規劃 zerojudge - d652

  1. 某一區間合併到最後會剩下最左邊和最右邊兩個數
  2. dp[L][R]代表將區間[L,R]合併到剩下區間端點兩數的最小代價
  3.  
dp[L][R] = max_{i=L}^{R}\{dp[L][i] + dp[i][R]+w[L]\times w[i]\times w[R]\}
dp[L][R]=maxi=LR{dp[L][i]+dp[i][R]+w[L]×w[i]×w[R]}dp[L][R] = max_{i=L}^{R}\{dp[L][i] + dp[i][R]+w[L]\times w[i]\times w[R]\}

L

R

L

R

i

i

i

L

i

R

區間動態規劃 zerojudge - d652

  1. 觀察
    • 合併到最後會剩下最左邊和最右邊兩個數
    • 假如合併的最後一步剩下W[0], W[i], W[n-1],代表W[0]和W[i]之間都合併完、W[i]和W[n-1]之間也合併完
    • 你不知道哪個i會使代價最低
  2. dp[L][R]代表將區間[L,R]合併到剩下區間端點兩數的最小代價
  3. 根據定義的dp陣列和觀察
dp[L][R] = max_{i=L}^{R}\{dp[L][i] + dp[i][R]+w[L]\times w[i]\times w[R]\}
dp[L][R]=maxi=LR{dp[L][i]+dp[i][R]+w[L]×w[i]×w[R]}dp[L][R] = max_{i=L}^{R}\{dp[L][i] + dp[i][R]+w[L]\times w[i]\times w[R]\}

區間動態規劃 zerojudge - d652

#include <...>

const int N = ???;
int dp[N][N];

int DP(int l, int r) {
    if(dp[l][r] != -1)    // 已經 DP過
        return dp[l][r];
    if(邊界條件) {
        dp[l][r] = 0;
        return dp[l][r];
    }
    dp[l][r] = 1e9 + 1;
    for(int i = l + 1; i < r; i++)
        dp[l][r] = min(dp[l][r], DP(l, i) + DP(i, r) + cost(l, i, r));
    return dp[l][r];
}

int main () {
    // 輸入
    memset(dp, -1, sizeof(dp));
    printf("%d\n", DP(0, n - 1));
}

區間動態規劃 zerojudge - d652

#include <...>

const int N = ???;
int dp[N][N];

int main () {
    // 輸入
    memset(dp, 0, sizeof(dp));
    for(int len = 3; len <= n; len++)
        for(int l = 0; l + len - 1 < n; l++) {
            int r = l + len - 1;
            dp[l][r] = 1e9;
            for(int i = l+1; i < r; i++)
                dp[l][r] = min(dp[l][r], dp[l][i] + dp[i][r] + cost(l, i, r));
        }
    printf("%d\n", dp[0][n - 1]);
}

區間動態規劃 zerojudge - d652

時間複雜度:

  • 每個區間可以有n種子區間的組合,因此轉移一次要花
  • 總共有      個區間(n個左界和n個右界)
  • 複雜度為
n^2
n2n^2
O(n)
O(n)O(n)
O(n^3)
O(n3)O(n^3)

更多區間動態規劃

uva10304:

題目大意:

權重由小到大給二元搜尋樹上每個葉子被訪問的次數e,每一次訪問的代價是葉子的深度d,所以一個葉子的總代價在二元樹中的代價為d*e,請找出一顆二元搜尋樹,使得訪問每個葉節點的總代價最低

區間動態規劃 四邊形不等式優化 HDU 3506

1D/1D 斜率優化DP HDU 3507

更多動態規劃

滾動數組DP UVA 12484

四邊形不等式優化

  • a < b ≤ c <d, f(a,c) + f(b,d) ≤ f(a,d) + f(b,c)

  • 假設k<d,如果有f(L,i)+f(i,R)≥f(L,d)+f(d,R),其中 d 是轉移 f(L, R) 最好的斷點(也稱為K(L, R)),則小於 d 的 i 當然結果都會比用 d 轉來的差。

  1. a < b ≤ c <d,  f(a,d) + f(b,c) ≧ f(a,c) + f(b,d)

  2. 假設i<d,如果有f(L,i)+f(i,R)≥f(L,d)+f(d,R),其中 d 是轉移 f(L, R) 最好的斷點(也稱為K(L, R)),則小於 d 的 i 當然結果都會比用 d 轉來的差。

  • 由式1可以得到 f(L+1,i)+f(L,d)≥f(L+1,d)+f(L,i);(L<L+1<i<d)
  • ⇒f(L+1,i)+f(i,R)+f(L,d)+f(d,R)≥f(L+1,d)+f(d,R)+f(L,i)+(i,R), 兩邊同加 f(i,R)+f(d,R)
  • ⇒(f(L+1,i)+f(i,R)+f(L,d)+f(d,R))−(f(L+1,d)+f(d,R)+f(L,i)+f(i,R))≥0
  • ⇒(f(L+1,i)+f(i,R))−(f(L+1,d)+f(d,R))≥(f(L,i)+f(i,R))−(f(L,d)+f(d,R))
  • 因為式2, (f(L,i)+f(i,R))−(f(L,d)+f(d,R))≥0
  • (f(L+1,i)+f(i,R))−(f(L+1,d)+f(d,R))≥0
  • f(L+1,i)+f(i,R)f(L+1,d)+f(d,R) 重要結論
I(L,R-1) \leq d
I(L,R1)dI(L,R-1) \leq d
d\leq K(L+1,R)
dK(L+1,R)d\leq K(L+1,R)
f(L+1,i)+f(i,R) \geq f(L+1,d)+f(d,R)
f(L+1,i)+f(i,R)f(L+1,d)+f(d,R)f(L+1,i)+f(i,R) \geq f(L+1,d)+f(d,R)
i < d
i<di < d

因為

  •  
  •  

所以

  •  
  •  
I(L,R-1) \leq d \leq K(L+1,R)
I(L,R1)dK(L+1,R)I(L,R-1) \leq d \leq K(L+1,R)

結論

  •  

狀態壓縮DP

  1. 最佳子結構、重複子問題

  2. 表達狀態的變數過多,無法只用兩三個維度的陣列表達

  3. 每個變數只有0和1兩個值

狀態壓縮DP zerojudge-d879

題目:

給2n(n<=8)個人,每個人位於平面上的一個點,希望將這2n個人組隊,每隊兩個人,組成一個隊伍的成本是兩個人之間的距離,求把所有人分組最小的成本

方法1:

用DFS直接爆搜,每層遞迴先選擇一個待分組的人當固定的成員,再依序枚舉沒有被分派組別的人給這個固定成員選組成一隊,繼續遞迴。

原理:

每個人最終會屬於某一組,所以每層遞迴可以固定一個人

狀態壓縮DP zerojudge-d879

狀態壓縮DP zerojudge-d879

...

發現重複子問題5,6,7,8

1

2

3

4

1

2

3

4

5

6

7

8

5

6

7

8

狀態壓縮DP zerojudge-d879

  • 用1表示還沒有被分組,0表示已經有組

  • 上一張投影片的相同子狀態可以表示成00001111

  •  
(15)_{10} = (00001111)_2
(15)10=(00001111)2(15)_{10} = (00001111)_2

狀態壓縮DP zerojudge-d879

#include <...>
...

void DP(int state) {
    int fix;
    for(fix = 0; fix < n; fix++)
        if(state & (1 << fix))            // 如果第fix個bit不是0
            break;
    state &= ~(1 << fix);                        // 把第fix個bit變0
    for(int i = 0; i < n; i++)
        if(state & (1 << i)) {
            int new_state = state & ~(1 << i);   // 把第i個bit變0
            if(new_state 還沒dp過)         // 避免重複子問題
                DP(new_state);
            // state的最佳解可能是new_state的最佳解加上fix和i同組的cost
            dp[state] = min(dp[state], dp[new_state] + cost(fix, i));
        }
}

int main() {
    ...
    // 輸入
    DP((1 << n) - 1);                     // (1 << n) - 1 二進位會是n個1
    cout << dp[(1 << n) - 1] << endl;
}

更多狀態壓縮

zerojudge-d688 无向图中子图的个数

題目:

給n(<=20)個點的一般圖,求其連通子圖的數量

zerojudge-c239 旅行者_九國遊歷記<4> 小綠去焱國

題目:

給n(<=15)個點且邊上有權重的一般圖,求從任一點開始經過每一個點回到起點的最短路徑和最長路徑

tioj-1908 大根蘿蔔 2015 TOI 第一次模考第1題

題目:

一塊nxn(n<=22)的蘿蔔田,每個格子裡都有一顆蘿蔔和其價值,拔起一顆蘿蔔會破壞四周的其他八顆蘿蔔,求可以收成的最大價值

更多狀態壓縮

  • 輪廓線動態規劃

動態規劃

By w86763777

動態規劃

  • 699