問題描述:
一塊金條切成兩半,是需要花費和長度數值一樣的銅板的。比如長度爲20的金條,不管切成長度多大的兩半,都要花費20個銅 板。一羣人想整分整塊金條,怎麼分最省銅板?
例如,給定數組{10,20,30},代表一共三個人,整塊金條長度爲 10+20+30=60。金條要分成10,20,30三個部分。 如果, 先把長 度60的金條分成10和50,花費60;再把長度50的金條分成20和30, 花費50;一共花費110銅板。 但是如果, 先把長度60的金條分成30和30,花費60;再把長度30金條分成10和20,花費30;一共花費90銅板。 輸入一個數組,返回分割的最小代價。
思路:
這個題是哈夫曼編碼問題,想把金條切成規定的多少段,選擇一個怎樣的順序能讓代價最低。我們可以認爲每一塊金條長度是一個葉節點,怎麼決定葉節點的合併順序才能讓整體的合併代價最低。而兩個葉節點合併之後產生的和就是它的合併代價。
也就是說,這個題是求所有非葉節點的值加起來最低。這個題整體就轉化爲:給了葉節點,選擇一個什麼合併順序,能夠導致非葉節點整體的求和最小。所以解題時可以反過來,把“一整條金條該如何切割”,換爲“已知需要切割的長度,如何使之加起來的代價最小”。
步驟:
- 先把需要分割的長度值,加入小根堆;
- 取出小根堆裏兩個最小的值,合併後的值再加入小根堆;
- 一直重複第二步,直到堆裏沒有值,可得到最小的和。
function lessMoney(arr){
if (!arr || arr.length < 1) {
return;
};
let res = 0;
while(arr.length > 1){
let cur = pollHeap(arr) + pollHeap(arr);
res += cur;
addHeap(arr,cur,arr.length-1);
}
return res;
}
function minHeap(arr){ // 建立小根堆
for(let i = 0; i < arr.length; i++){
while(arr[i] < arr[parseInt((i - 1)/2)]){
swap(arr,i,parseInt((i - 1)/2)); // 交換位置
i = parseInt((i - 1)/2);
}
}
};
function pollHeap(arr){ // 取出一個值
minHeap(arr);
return arr.shift();
};
function addHeap(arr,cur){ // 加入一個值
arr.push(cur);
let i = arr.length-1;
while(arr[i] < arr[parseInt((i - 1)/2)]){
swap(arr,i,parseInt((i - 1)/2))
i = parseInt((i - 1)/2);
}
}
function swap(arr,i,j){ // 交換位置
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
console.log(lessMoney([20,30,10,50])); // 200
// 10+20=30 30+30=60 60+50=110; 30+60+110=200
個人理解,直接使用排序數組也能解決。如果還有更好的解法,歡迎指教!