算法十八:背包问题2

描述

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;
}
// ================= 代码实现结束 =================







猜你喜欢

转载自blog.csdn.net/wydyd110/article/details/80898305