Dynamic Programming (3)
by music960633
課程內容 課程內容
• 矩陣快速冪優化
• 狀態壓縮
• 資料結構的優化
• 有限背包問題的優化
矩陣快速冪優化 矩陣快速冪優化
• 已知一 N*N 的矩陣 A ,可以用 Divide and Conquer 的方法在 O(N
3log(k)) 時間求得 Ak
• 這跟 DP 有什麼關係?
矩陣快速冪優化 矩陣快速冪優化
• 用 1*2 的骨牌填滿 2*n 的格子,共有幾種排法?
• f(n) =f(n-1)+f(n-2)
• f(n-1)=f(n-1)
•
矩陣快速冪優化 矩陣快速冪優化
• 可以用矩陣快速冪求出
• 如此可以在 O(log(n)) 時間求出 f(n) 的值
•
矩陣快速冪優化 矩陣快速冪優化
• 將 n 個排成一列的格子塗上紅、綠、藍三種顏色,且藍綠不可相 鄰,問有幾種塗法?
• f(n,0)=f(n-1,0)+f(n-1,1)+f(n-1,2)
• f(n,1)=f(n-1,0)+f(n-1,1)
• f(n,2)=f(n-1,0)+f(n-1,2)
•
狀態壓縮 狀態壓縮
• Travelling Salesman Problem(TSP)
• 求一張圖上權重最小的 Hamilton Circuit
• 已知此問題為 NP-Hard ,暴力做法為 O(n!) ,有沒有快一點的做 法呢?
狀態壓縮 狀態壓縮
• 如果只有 5 個點,可以怎麼做?
• 定義狀態: dp[n][s0][s1][s2][s3][s4]
• n 表示目前在 n 號節點上
• si 表示是否走過第 i 號節點 (0/1)
• 狀態轉移:
• dp[2][0][1][1][1][1]=min(dp[1][0][1][0][1][1]+dis[1][2], dp[3][0][1][0][1][1]+dis[3][2], dp[4][0][1][0][1][1]+dis[4][2])
• 答案: dp[0][1][1][1][1][1]
狀態壓縮 狀態壓縮
• 如果點數再多一點,例如 15 個點呢?
• 狀態: dp[][][][][][][][]...[][]
• 太累了!
• 只要記錄有沒有走訪過 (0/1) ,因此可以使用位元運算
• 定義狀態: dp[n][s]
• n 表示目前在 n 號點上
• s 表示目前走過哪些點
• 狀態數量: n*2n
狀態壓縮 狀態壓縮
• 狀態轉移:
• dp[n][s]=min(dp[i][s-(1<<n)]+dis[i][n]),
for all i such that (s & (1<<i)) != 0
• 時間複雜度: O(n)
• 答案: dp[0][(1<<N)-1]
• 整體時間複雜度: O(n2*2n)
• PS: 注意初始值的設定
資料結構的優化 資料結構的優化
• 回顧之前看過的問題:
• 給定一整數陣列,求取出數字的總合最大,且滿足兩數距離不能 小於 K
• 狀態: dp[n]: 從前 n 個數中取數,且有取到 arr[n] 的最大總 合
• 轉移: dp[n]=arr[n]+max(dp[n-K],dp[n-K-1],...,dp[1])
• 答案: max(dp[1],dp[2],...,dp[N])
• 時間複雜度: O(n2)
資料結構的優化 資料結構的優化
• 再令 dpMax[n]=max(dp[1],dp[2],...,dp[n])
• 狀態: dpMax[n] : 從前 n 個數中取數字的總合最大值
• 轉移: dpMax[n]=max(dpMax[n-1],dp[n])
=max(dpMax[n-1],arr[n]+dpMax[n-K])
• 答案: dpMax[N]
• 時間複雜度: O(n)
資料結構的優化 資料結構的優化
• 稍微改一下問題:
• 給定一整數陣列,求取出數字的總合最大,且滿足兩數距離不能 大於 K
• 狀態: dp[n]: 從前 n 個數中取數,且有取到 arr[n] 的最大總 合
• 轉移: dp[n]=arr[n]+max(dp[n-1],dp[n-2],...,dp[n-K],0)
• 答案: max(dp[1],dp[2],...,dp[N])
• 時間複雜度: O(n2)
資料結構的優化 資料結構的優化
• 再令 dpMax[n]=max(dp[1],dp[2],...,dp[n])
• 狀態轉移裡面是 max(dp[n-1]~dp[n-K]) ,取這個 max 沒意義啊
• 囧 ... 好吧,那就令 dpMax[n]=max(dp[n],...,dp[n-K+1])
• 發現 dpMax[n] 和 dpMax[n-1] 好像沒什麼關係,結果還是得重算
• 有其他辦法加速嗎?
資料結構的優化 資料結構的優化
• 方法 1 :
• 我們發現我們要需要取的是一個區間的 max
• 線段樹!
• 時間複雜度: O(nlog(n))
• 缺點:線段樹比較複雜,也比較難寫
資料結構的優化 資料結構的優化
• 方法 2 :
• 注意到取 max 的左界是遞增的,也就是說,只要一個位置跑到範圍外 面之後,之後就再也不會被取到了
• 使用 heap 取最大值
• push :記錄該點的值和位置
• top :如果取到的值已經「過期」了則直接丟掉,直到 top 不是過期的
• 時間複雜度: O(nlog(n))
• 優點: heap 好寫多了,甚至還有 stl
資料結構的優化 資料結構的優化
• 方法 3
• 注意到如果存在 i,j 使得 i<j 且 dp[i]<dp[j] ,則 dp[i] 就永遠 不會被取到了
• 只要記錄「可能成為 max 的 dp[] 」就好了
• 實做:
• 假設有個神祕的容器,每次放進 (dp[n],n)
• push: 將所有「 i<n 且 dp[i]<dp[n] 」的元素丟掉,再放入 (dp[n], n)
• top : 先將所有「過期」的元素丟掉,接著取出 dp 值最大的元素
資料結構的優化 資料結構的優化
• 方法 3
• 我們可以發現,對於任意兩個容器內的元素 (dp[i],i) 和 (dp[j]
,j) ,一定滿足:若 i<j ,則 dp[i]>dp[j]
• 先將這些元素依照 dp 值的順序放入陣列裡,然後觀察 push 、 po p 和 top 做了哪些事:
• push: 從 dp 值較小的一端,一直將元素 pop 出來,直到這端的第一個元 素的 dp[i]>dp[n] ,接著將 (dp[n],n) 推進陣列
• top : 從 dp 值較大的一端,一直將元素 pop 出來,直到這端的第一個元 素是還沒過期的,接著回傳這端的一個元素的 dp 值 (max)
資料結構的優化 資料結構的優化
• ex: arr[] = {3,-2,-1,-2,2}, K=3
()
push (3,1)
資料結構的優化 資料結構的優化
• ex: arr[] = {3,-2,-1,-2,2}, K=3 (3,1)
push (3,1)
資料結構的優化 資料結構的優化
• ex: arr[] = {3,-2,-1,-2,2}, K=3 (3,1)
push (-2+3,2)
資料結構的優化 資料結構的優化
• ex: arr[] = {3,-2,-1,-2,2}, K=3 (3,1) (1,2)
push (-2+3,2)
資料結構的優化 資料結構的優化
• ex: arr[] = {3,-2,-1,-2,2}, K=3 (3,1) (1,2)
push (-1+3,3)
資料結構的優化 資料結構的優化
• ex: arr[] = {3,-2,-1,-2,2}, K=3 (3,1) (2,3)
push (-1+3,3)
資料結構的優化 資料結構的優化
• ex: arr[] = {3,-2,-1,-2,2}, K=3 (3,1) (2,3)
push (-2+3,4)
資料結構的優化 資料結構的優化
• ex: arr[] = {3,-2,-1,-2,2}, K=3 (3,1) (2,3) (1,4)
push (-2+3,4)
資料結構的優化 資料結構的優化
• ex: arr[] = {3,-2,-1,-2,2}, K=3 (3,1) (2,3) (1,4)
資料結構的優化 資料結構的優化
• ex: arr[] = {3,-2,-1,-2,2}, K=3 (2,3) (1,4)
push (2+2,5)
資料結構的優化 資料結構的優化
• ex: arr[] = {3,-2,-1,-2,2}, K=3 (4,5)
push (2+2,5)
資料結構的優化 資料結構的優化
• 方法 3
• 可以使用陣列或 deque 實做
• 時間複雜度
• 每個元素只會被 push 一次
• 每個元素只會被 pop 一次
• O(n)!
• 該方法稱作「單調隊列優化」
資料結構的優化 資料結構的優化
• 再看一個例題
• 円円想在一條筆直的路上開設一些漢堡店,已知他取了 N 個等間 隔的點做了評估,得到在每個點開店可以賺的金額 val[i]( 負的 表示賠錢 ) 。另外,如果開設超過一間店,則兩間相鄰的店面不 能相距超過 K 單位,且這兩家店之間需要交通成本,金額為 (c
* 距離 ) 。求円円最多可以賺多少錢?
資料結構的優化 資料結構的優化
• 狀態:
• dp[n] 表示在第 1~n 個點開店,且第 n 個點有開店的最大值
• 轉移:
• dp[n]=val[n]+max(dp[i]-c*(n-i)), n-K ≦ i ≦ n-1
• 時間複雜度: O(NK)
資料結構的優化 資料結構的優化
• 優化
• 改寫一下轉移式:
• dp[n]=val[n]+max(dp[i]-c*n+c*i)), n-K ≦ i ≦ n-1
• dp[n]=(val[n]-c*n)+max(dp[i]+c*i), n-K ≦ i ≦ n-1
• 令 t[i]=dp[i]+c*i
• dp[n]=(val[n]-c*n)+max(t[i]), n-K ≦ i ≦ n-1
• 「 max(t[i]), n-K ≦ i ≦ n-1 」可以使用單調隊列優化!
• 時間複雜度: O(N)
• PS: 丹丹漢堡很好吃 XD
有限背包問題的優化 有限背包問題的優化
• 有一個可以耐重 W 的背包,及 N 種物品,每種物品有各自的重量 w[i] 和價值 v[i] ,且數量為 k[i] 個,求在不超過重量限制的情 況下往背包塞盡量多的東西,總價值最大為多少?
• 做法:將 k[i] 個相同物品視為不同物品,做 0/1 背包
• 問題:真的需要分成 k[i] 那麼多個嗎?
有限背包問題的優化 有限背包問題的優化
• 對於同一種物品,不知道取幾個是最佳解,而分成個別的物品一 定能找到最佳解
• 想法:如果可以將 k[i] 個同種物品分成幾堆,且可以經由不同 的組合湊出 1~k[i] 每種組合就可以了!
• ex: k[i]=6 ,此時把 6 個相同物品分成 {1,2,3} 三堆,可以發現
• 1=1 / 2=2 / 3=3 / 4=1+3 / 5=2+3 / 6=1+2+3
• 如此只要分成 3 堆也可以得到最佳解
有限背包問題的優化 有限背包問題的優化
• 要怎麼分堆呢?
• 二進位!
• 將 k[i] 分成 {1,2,4,8,...,2p,q} ,其中 p 為使 2p+1-1 不大於 k[i] 的最大整數
• ex: 21 可以分成 {1,2,4,8,6}
• 為什麼這樣分可以湊出所有的組合?
• 若 x<2p+1,則一定可以用 {1,2,4,...,2p} 湊出 x
• 若 x≧2p+1,則先取 q ,剩下的 x-q<2p+1,就可以用 {1,2,4,...,2p} 湊出
• 時間複雜度
• 最多分出 log(k[i])+1 堆
• O(NW*logK)
有限背包問題的優化 有限背包問題的優化
• 單調隊列優化
• 觀察轉移式
• dp[n][m]=max{dp[n-1][m-w[n]*i]+v[n]*i}, 0 ≦ i ≦ k[n]
• dp[n][m]=max{dp[n-1][j]+v[n]*(m-j)/w[n]},
j=m,m-w[n],...,m-w[n]*k[n]
• 分別對所有除 w[n] 餘數相同的索引值做單調隊列優化 DP ,一共 做 w[n] 次,可以得到 O(NW) 的演算法