置頂

我的 VPython 教學文件 (HackMD 版本)

VPython 教學文件目錄 安裝及測試 基本語法 等速度直線運動 自由落下 終端速度 水平抛射 使用For迴圈計算水平抛射資料 斜向抛射 圓周運動 簡諧運動 單擺 木塊彈簧系統分離 重力及簡諧 行星運動 相疊木塊 雙重簡諧運動 一維彈性碰撞 ...

熱門文章

2026年10月18日 星期日

ZeroJudge 解題筆記:o628.天秤 (Libra)

作者:王一哲
日期:2026年10月18日


ZeroJudge 題目連結:o628.天秤 (Libra)

題目 pdf 檔連結:天秤 (Libra)

解題想法


這題是TOI練習賽202409潛力組第3題,這組題目當中最簡單的一題。題目是多組測資。每組測資第1列為1個整數 $n (2 \leq n \leq 100)$,代表有 $n$ 個砝碼,第2列有 $n$ 個正整數 $w_1, w_2, w_3, \dots, w_n ~(w_i \leq 10^3)$,代表這 $n$ 個砝碼的重量。題目要檢測,是否能將 $n$ 個砝碼分為總重量相同的兩組。

先計算砝碼總重量 $wsum$,如果 $wsum$ 是奇數,不可能平分,直接輸出 $0$。接下來要湊出 $target = wsum // 2$,因為每個砝碼只有選或不選兩種可能性,是 0/1 背包問題,可以用一維陣列 $dp$ 記錄狀態,$dp[j]$ 是否可以湊出總重量 $j$,也可以用 bitset 取代陣列。 。

Python 程式碼


使用時間約為 0.4 s,記憶體約為 9.9 MB,通過測試。
def solve():
    import sys

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        ptr += 1
        weights = list(map(int, data[ptr:ptr+n]))
        ptr += n
        wsum = sum(weights)
        if wsum%2 == 1:  # 不可能平分,輸出 0
            result.append("0\n")
            continue
        target = wsum//2  # 目標
        dp = [False]*(target+1)  # 0/1 背包問題,是否能湊出 target
        dp[0] = True
        for w in weights:
            for j in range(target, w-1, -1):
                dp[j] |= dp[j-w]
        result.append("1\n" if dp[target] else "0\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


使用時間約為 14 ms,記憶體約為 9.6 MB,通過測試。
def solve():
    import sys

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        ptr += 1
        weights = list(map(int, data[ptr:ptr+n]))
        ptr += n
        wsum = sum(weights)
        if wsum%2 == 1:  # 不可能平分,輸出 0
            result.append("0\n")
            continue
        target = wsum//2  # 目標
        dp = 1  # 0/1 背包問題,是否能湊出 target
        for w in weights:
            dp |= (dp << w)
        result.append("1\n" if (dp >> target) & 1 else "0\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


C++ 程式碼


使用時間約為 9 ms,記憶體約為 3.6 MB,通過測試。
#include <iostream>
#include <vector>
using namespace std;

int main() {
    ios::sync_with_stdio(0); cin.tie(0);
    int n;  // n 個砝碼
    while(cin >> n) {
        int w, wsum = 0;  // 重量,重量加總
        vector<int> weights (n);  // n 個砝碼的重量
        for(int i = 0; i < n; i++) {
            cin >> w;
            weights[i] = w;
            wsum += w;
        }

        // 特例,如果 wsum 是奇數,無法平分
        if (wsum % 2 == 1) {
            cout << "0\n";
            continue;
        }

        // 0/1 背包問題
        int target = wsum / 2;
        vector<bool> dp (target + 1, false);
        dp[0] = true;  // 一定可以湊出總重量 0
        for(int w : weights) {
            for(int j = target; j >= w; j--) {
                if (dp[j - w]) dp[j] = true;
            }
        }

        // 如果 dp[target] == true,有解
        cout << dp[target] << "\n";
    }
    return 0;
}


使用時間約為 2 ms,記憶體約為 3.6 MB,通過測試。
#include <iostream>
#include <vector>
#include <bitset>
using namespace std;

int main() {
    ios::sync_with_stdio(0); cin.tie(0);
    int n;  // n 個砝碼
    while(cin >> n) {
        int w, wsum = 0;  // 重量,重量加總
        vector<int> weights (n);  // n 個砝碼的重量
        for(int i = 0; i < n; i++) {
            cin >> w;
            weights[i] = w;
            wsum += w;
        }

        // 特例,如果 wsum 是奇數,無法平分
        if (wsum % 2 == 1) {
            cout << "0\n";
            continue;
        }

        // 0/1 背包問題
        int target = wsum / 2;
        bitset<50001> dp;  // 題目總重量上限 100 * 1000 = 100000,目標上限 50000
        dp.set(0);  // 一定可以湊出總重量 0
        for(int w : weights) {
            dp |= (dp << w);
        }

        // 如果 dp[target] == true,有解
        cout << dp[target] << "\n";
    }
    return 0;
}


C 語言程式碼


使用時間約為 5 ms,記憶體約為 1.9 MB,通過測試。
#include <stdio.h>

int main() {
    int n;  // n 個砝碼
    while(scanf("%d", &n) != EOF) {
        int w, wsum = 0;  // 重量,重量加總
        int weights[100] = {0};  // n 個砝碼的重量,題目上限 100 個
        for(int i = 0; i < n; i++) {
            scanf("%d", &w);
            weights[i] = w;
            wsum += w;
        }

        // 特例,如果 wsum 是奇數,無法平分
        if (wsum % 2 == 1) {
            puts("0");
            continue;
        }

        // 0/1 背包問題
        int target = wsum / 2;
        int dp[50001] = {0};  // 題目總重量上限 100 * 1000 = 100000,目標上限 50000
        dp[0] = 1;  // 一定可以湊出總重量 0
        for(int i = 0; i < n; i++) {
            for(int j = target; j >= weights[i]; j--) {
                if (dp[j - weights[i]]) dp[j] = 1;
            }
        }

        // 如果 dp[target] == 1,有解
        printf("%d\n", dp[target]);
    }
    return 0;
}


沒有留言:

張貼留言