描述
n个物品,每个物品有一个体积v和价值w。现在你要回答,把一个物品丢弃后,剩下的物品装进一个大小为V的背包里能得到的最大价值是多少。
输入
输入的第一行包含一个正整数n(n ≤ 5000)。
接下来n行,每行包含两个正整数v和w(v,w ≤ 5000),分别表示一个物品的体积和价值。
接下来一行包含一个正整数q(q ≤ 5000),表示询问个数。
接下来q行,每行包含两个正整数V和x(V ≤ 5000,x ≤ n),表示询问将物品x丢弃以后剩下的物品装进一个大小为V的背包能得到的最大价值。
输出
输出q行,每行包含一个整数,表示询问的答案。
样例输入
3
3 5
2 2
1 2
3
3 1
3 2
3 3
样例输出
4
5
5
样例解释
有3个物品,第一个物品的体积为3、价值为5,第二个物品体积为2、价值为2,第三个物品体积为1、价值为2。
有3个询问:
第一个询问是问去掉1物品后剩下的2、3物品填进一个大小为3的背包能得到的最大价值。显然2、3物品都是可以放进背包的,所以最大价值为2+2=4。
第二个询问是问去掉2物品后剩下的1、3物品填进一个大小为3的背包能得到的最大价值。若我们填3物品,我们只能得到价值2;若我们填1物品,则可以得到价值5。所以最大价值为5。
第三个询问我们同样也是填1物品,最大价值为5。
提示
[我们可以预处理“前缀背包”、“后缀背包”,然后询问时做“背包合并”的操作。]
一. 伪代码
二. 具体实现(C++)
#include <bits/stdc++.h>
using namespace std;
// ================= 代码实现开始 =================
const int N = 5005;
int d[N][N],f[N][N];
// n个物品,每个物品有体积价值,求若扔掉一个物品后装进给定容量的背包的最大价值
// n:如题
// w:长度为n+1的数组,w[i]表示第i个物品的价值(下标从1开始,下标0是一个数字-1,下面同理)
// v:长度为n+1的数组,v[i]表示第i个物品的体积
// q:如题
// qV:长度为q+1的数组,qV[i]表示第i次询问所给出的背包体积
// qx:长度为q+1的数组,qx[i]表示第i次询问所给出的物品编号
// 返回值:返回一个长度为q的数组,依次代表相应询问的答案
vector<int> getAnswer(int n, vector<int> w, vector<int> v, int q, vector<int> qV, vector<int> qx) {
//计算前缀背包
for(int i= 1; i<=n; ++i){
for(int V=0; V<v[i]; ++V)
d[i][V] = d[i-1][V];
for(int V=v[i]; V<=5000; ++V)
d[i][V] = max(d[i-1][V],d[i-1][V-v[i]]+w[i]);
}
//计算后缀背包
for(int i = n; i>=1; --i){
for(int V=0; V<v[i]; ++V)
f[i][V] = f[i+1][V];
for(int V=v[i]; V<=5000; ++V)
f[i][V] = max(f[i+1][V],f[i+1][V-v[i]]+w[i]);
}
vector<int> ans;
for(int k=1; k<=q; ++k){
int x = qx[k],V=qV[k];
int mx =0;
for(int i=0;i<=V;++i)
mx = max(mx,d[x-1][i] + f[x+1][V-i]);
ans.push_back(mx);
}
return ans;
}
// ================= 代码实现结束 =================