快轉到主要內容

CSES-1161 Stick Divisions

目錄


題目連結:https://cses.fi/problemset/task/1161

題意
#

這題說你有一根長度為 \(x\) 的木棍,想要把它切成 \(n\) 段指定長度的小木棍。
每次切割一根木棍時,花費的成本就是那根木棍「切之前的長度」。
請找出達成目標所需的最小總成本。

思路
#

如果照著題目的順序思考「怎麼切」,情況會變得非常複雜。
這類問題通常可以反向思考。
也就是把「切木棍」變成「黏木棍」。

想像現在地上散落著 \(n\) 根小木棍。
我們每次挑兩根黏在一起,直到剩下最後一根長度為 \(x\) 的大木棍。

黏合的成本,就是那兩根木棍的總長度。
要讓總計成本最小,這其實是一個標準的貪婪演算法套用場景,概念類似霍夫曼樹(Huffman Tree)。

我們每次都貪心地挑選當下「最短的兩根木棍」來合併。
這樣長度較長的木棍就不會被重複計算太多次。

實作上,只要開一個 Min-Heap(在 C++ 就是 priority_queue 搭配 greater)。
不斷抽出最小的兩個數字相加,再把總和塞回 Heap 裡。
直到只剩下一個元素就算完成了。

程式碼
#

#include <bits/stdc++.h>
using namespace std;
 
#define pb push_back
#define fi first
#define se second
#define INF LONG_LONG_MAX/1000
#define WA() cin.tie(0)->sync_with_stdio(0);
#define all(x) (x).begin(), (x).end()
#define int long long
#define PII pair<int, int>
 
signed main() { WA();
    int n, x, t, sum = 0; cin >> x >> n; // x 為初始總長度,n 為最終小木棍數量
    
    // 建立 Min-Heap (最小優先佇列),確保每次 pop 出來的都是最小的元素
    priority_queue<int, vector<int>, greater<int>> pq;
    
    // 讀入每根小木棍的目標長度,並把它們全部丟進 Min-Heap 裡
    while (n--) cin >> t, pq.push(t);
    
    // 不斷合併,直到只剩下一根大木棍為止
    while (pq.size() != 1) {
        // 取出當前最短的兩根木棍
        int a = pq.top(); pq.pop();
        int b = pq.top(); pq.pop();
        
        // 將合併成本加到總計成本中
        sum += a+b;
        
        // 將合併後的新木棍放回 Min-Heap 參與後續的合併
        pq.push(a+b);
    }
    cout << sum;
}
Piau 的筆記本
作者
Piau 的筆記本
希望我寫下來的東西能夠長久的記在我的腦中