TIOJ 2135:背包問題
TIOJ 2135:背包問題
題目大意:有 $N$ 個物品跟一個容量 $W$ 的背包,每個物品有 $w,v,c$,代表大小 $w$、價值 $v$,除了整個拿以外,也可以花 $c$ 的錢拿 $v\times\frac{w'}{w}$,有 $Q$ 次修改,每次會改某個物品的 $c$,修改完回答最大收益。
解法:發現最多只會花費一個物品的 $c$,可以分治求出不包含某個物品的 $dp$ 值,枚舉切多少後用 $\text{multiset}$ 存答案。
$\text{Code:}$
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
const int M = 2005;
int n, m, q;
double ans[N];
vector<int> op[M];
multiset<double> ms;
int w[N], v[N], c[N], dp[M];
void divide(int l, int r) {
if (l == r) {
if (l == 1) ms.insert(max(dp[m], w[1] <= m ? dp[m - w[1]] + v[1] : 0));
ans[l] = 0;
for (int i = 1; i <= min(m, w[l]); i++) ans[l] = max(ans[l], dp[m - i] + (double) v[l] * i / w[l]);
ms.insert(ans[l] - c[l]);
return;
}
int mid = (l + r) / 2;
for (int i = 0; i <= m; i++) op[i].emplace_back(dp[i]);
for (int i = mid + 1; i <= r; i++) for (int j = m; j >= w[i]; j--) dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
divide(l, mid);
for (int i = 0; i <= m; i++) dp[i] = op[i].back();
for (int i = l; i <= mid; i++) for (int j = m; j >= w[i]; j--) dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
divide(mid + 1, r);
for (int i = 0; i <= m; i++) dp[i] = op[i].back(), op[i].pop_back();
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int tt;
cin >> tt;
while (tt--) {
ms.clear();
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> w[i] >> v[i] >> c[i];
fill(dp + 1, dp + m + 1, 0);
divide(1, n);
cout << fixed << setprecision(7) << *ms.rbegin() << '\n';
cin >> q;
while (q--) {
int p, x;
cin >> p >> x;
ms.erase(ms.find(ans[p] - c[p]));
c[p] = x;
ms.insert(ans[p] - c[p]);
cout << fixed << setprecision(7) << *ms.rbegin() << '\n';
}
}
}
我的分享就到這裡結束了,如果喜歡我的 $\text{Blog}$,歡迎追蹤!
留言
張貼留言