快轉到主要內容

CSES-1092 Two Sets

目錄

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


題意
#

給定一個整數 \( n \),將 \( 1 \) 到 \( n \) 的數字拆分成兩個集合,
使兩個集合的數字總和相同。

如果能成功平分,輸出 YES 與兩組集合的內容;
若無法平分,輸出 NO

思路
#

第一步先檢查數字總和能否被平分。
\( 1 \) 到 \( n \) 的總和為 \( \frac{n(n+1)}{2} \),
如果總和是奇數則無法平分,直接輸出 NO

如果總和是偶數,就一定能找到平分的方法。
目標是從 \( n \) 個數字中挑出部分,使總和等於整體的一半。

這裡可以使用貪心策略,從數值最大的 \( n \) 開始往下挑選。
若當前數字加入第一堆不會超過目標值,就將其加入;
若會超過,就放入第二堆。
依序選到 1 後就能成功分好兩組。

程式碼
#

#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; // 讀取數字 n
    
    // 判斷 1~n 的總和是否為奇數,奇數無法平分
    if ((n*(n+1)/2) & 1) cout << "NO\n";
    else {
        vector<int> a, b;
        int sum = 0; // sum 紀錄第一堆目前累積的總和
        
        // 從大到小遍歷 1~n 的數字,貪心地放入第一堆
        for (int i = n; i > 0; i--) {
            // 如果把 i 放進去不會超過總和的一半,就放進第一堆 a
            if (sum + i <= n*(n+1)/2/2) sum += i, a.pb(i);
            else b.pb(i); // 否則代表第一堆裝不下了,放進第二堆 b
        }
        
        cout << "YES\n" << sz(a) << '\n';
        for (auto i : a) cout << i << ' ';
        cout << '\n' << sz(b) << '\n';
        for (auto i : b) cout << i << ' ';
        cout << '\n'; // 依序印出兩堆集合的大小與內容
    }
}
Piau 的筆記本
作者
Piau 的筆記本
希望我寫下來的東西能夠長久的記在我的腦中