木板切割問題(二)——動態規划


一、問題引入

有一根長度為L(L < 1000)的木棍,還有n(n < 50)個切割點的位置(按照從小到大排列)。你的任務是在這些切割點的位置處把棍子切成n+1份,使得總費用最小。每次切割的費用等於被切割的木棍長度。

二、問題分析

這個問題很像前面的柵欄維修(給定n個木棍的長度,切割點任意),這道題目相當於給定n+1個木棍的長度,且切割點固定。之前的貪心法就不能適用,因為用貪心法需要切割的點不一定是給定的切割點。我們必須換一種思路了。

n的規模非常小,可以考慮枚舉切割點,但直接枚舉所有的切割順序肯定太大,可以考慮動態規划,枚舉切割第一刀的位置。

設d(i,j)為切割小木棍i~j的最優費用,則d(i,j) = min{(d(i,k),d(k,j)) | i < k < j} + a[j] - a[i],其中a[j] - a[i]代表第一刀的費用。

注意,這里i,j都是表示切割點的位置,而不是木棍的序號,這樣比較方便。

三、代碼實現

 1 #include<stdio.h>
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 
 7 const int INF = 0x3f3f3f3f;
 8 const int maxl = 1000 + 10;
 9 const int maxn = 50 + 10;
10 int L, n, cutp[maxn];
11 int d[maxn][maxn];    //d[i][j]表示切割小木棍i-->j的最小費用,i,j表示的是切割點,d[0][n + 1]為所求
12 
13 void slove()
14 {
15     for (int i = n + 1; i >= 0; i--)        //注意循環順序,這里必須是從大到小
16         for (int j = i; j <= n + 1; j++)    //這里必須從小到大,這個觀察狀態轉移方程就可以了
17         {
18             d[i][j] = (i + 1 == j ? 0 : INF);
19             for (int k = i + 1; k < j; k++)
20                 d[i][j] = min(d[i][j], d[i][k] + d[k][j] + cutp[j] - cutp[i]);
21         }
22     printf("The minimum cutting is %d.\n", d[0][n + 1]);
23 }
24 
25 int main()
26 {
27     while (scanf("%d", &L) == 1 && L)
28     {
29         scanf("%d", &n);
30         for (int i = 1; i <= n; i++)
31             scanf("%d", &cutp[i]);
32 
33         //左邊界編號為0,右邊界為 n + 1
34         cutp[0] = 0; cutp[n + 1] = L;
35 
36         slove();
37     }
38     return 0;
39 }

四、總結

這種解法,狀態有O(n2),每種狀態有O(n)個決策,時間復雜度為O(n3)。據說可以用四邊形不等式優化到O(n2),以后會了再補充吧。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM