快轉到主要內容

CSES-2183 Missing Coin Sum

標籤: 貪婪
目錄


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

題意
#

題目給定 \( n \) 個皆為正整數的硬幣面值。
需要從中挑選任意數量的硬幣組合,找出「無法湊出的最小正整數總和」。
例如硬幣面值為 1, 2, 5,可湊出 1, 2, 3 (=1+2),但無法湊出 4,因此答案為 4。

思路
#

本題可藉由觀察數字組合的單調性,使用連續區間擴充的「貪婪法(Greedy)」求解。

令變數 ans 代表「當前目標要湊出的最小正整數」。
因湊齊數字必定從 1 開始,初始時將 ans 設為 1。
若能湊出 1 到 ans-1 之間的所有整數,則加入一枚面值為 i 的硬幣後,可將湊出的區間擴充。

首先將所有硬幣從小到大排序。接著依序檢驗每枚硬幣的面值 i

  • 若硬幣面值 i 小於或等於 當前目標 ans
    表示這枚硬幣可以銜接既有的數字區間。
    原本可湊出 1ans-1,加入 i 後可湊出的最大值延伸至 (ans-1) + i
    因此,新的目標 ans 直接更新為 ans + i

  • 若硬幣面值 i 大於 當前目標 ans
    表示目前既有的硬幣組合加上新硬幣後,無法跨越 ans 產生連續數值(出現斷層)。
    此時無法湊出 ans,這個湊不出來的 ans 即為最終解答。

程式碼
#

#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; cin >> n;
    vector<int> v(n);
    for (auto &i : v) cin >> i;
    
    // 將所有硬幣面值從小到大排序
    sort(all(v));
    
    // ans 代表當前預期要湊出的最小目標數字,從 1 開始
    // 若成立則代表目前已能完美湊出 1 到 ans-1
    int ans = 1;
    
    for (auto &i : v) {
        // 超過當前目標數字時,表示出現斷層無法湊出
        if (i > ans) break;
        
        // 否則,銜接硬幣擴展可湊出的上限區間
        ans += i;
    }
    
    // 輸出第一個湊不出來的數字
    cout << ans << '\n';
}
Piau 的筆記本
作者
Piau 的筆記本
希望我寫下來的東西能夠長久的記在我的腦中