木板切割問題——貪心


一、問題引入

農夫約翰為了修理柵欄,要將一塊很長的木塊切成N塊。准備切成的長度分別是L1、L2、、、,LN,未切割前的木板長度切好為切割后木板長度的總和。每次切斷木板時的開銷是這塊木板的長度。(1 ≤ N ≤ 20000,0 ≤ Li ≤ 50000)

二、解題思路

由於N的值非常大,不可能枚舉所有情況再求解,必須用一種比較高效的算法。木板的切割循序不確定,看似自由度很高,是先選擇切出較短的,還是切較長的。如果我們把一種完全切割后的情況列舉出來,會發現可以用貪心算法

驚奇的發現,這種切法的切割費用之和 == 所有非葉子節點權值和 == 葉子節點的權值 * 深度(根節點深度為0)

即 33 = 15 + 7 + 8 + 3 = 3*2 + 4*2 + 5*2 + 1*3 + 2*3

問題轉化為,已知所有的葉子節點和根節點,求葉子節點的權值 * 深度和的最小值。

顯然,使權值大的深度小,權值小的深度大。於是,此時的最佳切割方法應該具有如下性質:

    最短版和次短板應該是兄弟節點

這一性質在切割過程中始終成立,反過來,我們可以根據這種性質建立起對應的二叉樹。即每次合並最小的,合並后的值加到總費用中。(注意建立的樹不唯一,但每種的結果一樣,所以選其中一種就行)

由於是每次取最小和次小合並,維護一個優先隊列就行

三、代碼實現

 1 #include<stdio.h>
 2 #include<iostream>
 3 #include<queue>
 4 using namespace std;
 5 
 6 typedef long long LL;
 7 const int maxn = 20000 + 10;
 8 int n, L[maxn];
 9 
10 void slove()
11 {
12     LL ans = 0;
13 
14     //聲明一個小值在前的優先隊列
15     priority_queue<int, vector<int>, greater<int>>que;
16     for (int i = 0; i < n; i++)
17         que.push(L[i]);
18 
19     //循環到只剩一塊模板為止
20     while (que.size() > 1)
21     {
22         //取出最短的木板和次短的木板
23         int len1, len2;
24         len1 = que.top(); que.pop();
25         len2 = que.top(); que.pop();
26 
27         ans += (len1 + len2);
28         que.push(len1 + len2);
29     }
30     printf("%lld\n", ans);
31 }
32 
33 int main()
34 {
35     while (scanf("%d",&n) == 1)
36     {
37         for (int i = 0; i < n; i++)
38             scanf("%d", &L[i]);
39         slove();
40     }
41     return 0;
42 }

四、總結

這個題運用了很多哈夫曼樹的思想,下一篇文章再討論吧。

 


免責聲明!

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



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