題意#
這題是非常經典的「0/1 背包問題」(0/1 Knapsack Problem)。
你來到一家書店,手上有 \( x \) 元的預算。店裡有 \( n \) 本書,每本書都有標價跟對應的頁數。
因為每本書都只有一本,你對每一本書只能選擇「要買」或「不買」。問題是:在不超過預算的前提下,你最多能買到總共多少頁的書?
思路#
我們可以使用動態規劃來解決這個問題。
定義一個一維陣列 dp,其中 dp[j] 代表「花費不超過 \( j \) 元時,能獲得的最大總頁數」。
針對每一本書,我們都有買或不買兩種選擇。假設當前這本書的價格是 w[i],頁數是 v[i],那我們更新 dp 陣列的方式就是:看是「不買這本書(維持原本的 dp[j])」比較好,還是「買這本書(加上這本書的頁數 v[i],並扣掉對應的預算 w[i],也就是 dp[j - w[i]] + v[i])」比較划算,我們取這兩者的最大值。
特別要注意的是,因為每本書只能買一次,我們在更新 dp 陣列時必須「從後往前」遍歷預算(從最大預算 \( x \) 一路扣減到該書的價格 w[i])。
這樣可以確保我們在更新 dp[j] 時,所參考到的 dp[j - w[i]] 是「還沒有考慮過當前這本書」的狀態,避免不小心把同一本書重複買了好幾次。
程式碼#
#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; cin >> n >> x;
vector<int> v(n), w(n), dp(x+1);
for (auto &i : w) cin >> i; // 讀取每本書的價格 (weight)
for (auto &i : v) cin >> i; // 讀取每本書的頁數 (value)
for (int i = 0; i < n; i++) {
// 從預算上限 x 倒敘遍歷到當前這本書的價格 w[i]
// 倒敘是為了確保前方的狀態尚未被當前這本書更新過,保證每本書只買一次
for (int j = x; j >= w[i]; j--) {
// 決定要不要買第 i 本書:
// 不買就是原本的 dp[j];買的話,就是剩餘預算的最大頁數加上這本書的頁數
dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
}
}
// dp[x] 即為在預算 x 內能買到的最大總頁數
cout << dp[x] << '\n';
}
