曾经沧海难为水,除却巫山不是云。|

Joey-Wang

园龄:4年3个月粉丝:17关注:0

11.7 背包问题

11.7 背包问题

http://codeup.hustoj.com/contest.php?cid=100000631

A 装箱问题

image-20200901191649727

题目解析

这道题其实就是01背包,01背包的二维状态转移方程:

令dp[i][v] 表示前i件物品恰好装入容量为v的背包中所能获得的最大价值
dp[i][v] = max{ dp[i-1][v] , dp[i-1][v-w[i]]+c[i]} (1<=i<=n, w[i]<=v<=V) ,边界:dp[0][v]=0(0<=v<=V)
化简为一维状态转移方程:【要逆序遍历v
dp[v] = max{ dp[v] , dp[v-w[i]]+c[i]} (w[i]<=v<=V),边界:dp[v]=0(0<=v<=V)

此题只有容量V和每个物品的体积,没有箱子的价值,所以这里也将箱子的体积作为箱子的价值,一维状态转移方程:
dp[i][v]表示前i个物品恰好装入容量为v的箱子中所能获得的最大体积
dp[v] = max{ dp[v] , dp[v-w[i]]+w[i]} (w[i]<=v<=V),边界:dp[v]=0(0<=v<=V)
最后遍历所有dp[v] (0<=v<=V) 找到最大的那个就是能获得的最大体积max_v
我现在觉得不用遍历所有的dp[v]。。。dp[V]就是答案,不明白为什么书上说要遍历所有的取最大值,我觉得dp[V]肯定是最大的

⚠️ 最后要求输出的是最小剩余空间,故为1-ans

代码

#include <cstdio>
#include <algorithm>

using namespace std;
#define maxV 20005 //体积上限
#define maxn 100 //物品上限

int main() {
    int V, n;
    int v[maxn];
    int dp[maxV];
    scanf("%d%d", &V, &n);
    for (int i = 1; i <= n; i++) scanf("%d", &v[i]);
    for (int i = 0; i <= V; i++) dp[i] = 0; //对应d[0][i]=0 前0间物品装i容量箱子的最大体积
    for (int i = 1; i <= n; i++) {
        for (int j = V; j >= v[i]; j--) {
            dp[j] = max(dp[j], dp[j - v[i]] + v[i]);
        }
    }
    //得到dp[n][j]前n件物品装j容量箱子的最大体积
    int ans = 0;
    for (int i = 0; i <= V; i++) {
        if (dp[i] > ans) ans = dp[i];
    }
    printf("%d\n", V - ans);
    return 0;
}

B 采药

image-20200901194210442

题目解析

这道题就是原原本本的01背包:
T总共能够用来采药的时间——背包的容量
M山洞里的草药的数目——物品的种类
采摘某株草药的时间和这株草药的价值——每项物品的重量和价值

PS:我觉得最后可以直接输出dp[V],这就是答案,不用遍历后取最大值

代码

#include <cstdio>
#include <algorithm>

#define maxn 105 //最大物品数
#define maxv 1005 //V上限
using namespace std;

int main() {
    int V, n;
    int w[maxn], c[maxn], dp[maxv];
    scanf("%d%d", &V, &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &w[i], &c[i]);
    }
    for (int i = 0; i <= V; i++) dp[i] = 0; //初始化dp[0][i]为前0件物品装入容量为i背包中的最大价值
    for (int i = 1; i <= n; i++) {
        for (int v = V; v >= w[i]; v--) {
            dp[v] = max(dp[v], dp[v - w[i]] + c[i]);
        }
    }
    int ans = 0;
    for (int i = 0; i <= V; i++) {
        if (dp[i] > ans) ans = dp[i];
    }
    printf("%d\n", ans);
    return 0;
}

C 货币系统 🌟🌟

image-20200901194523444

知识回顾

这道题是恰好装满的完全背包问题

1️⃣ 01背包 🆚 完全背包

  1. 一维的状态转移方程相同:dp[v] = max{ dp[v] , dp[v-w[i]]+c[i]} (w[i]<=v<=V)
    01背包要逆序遍历v,完全背包要顺序遍历v

  2. 完全背包的二维状态方程与01背包不同,这就是为啥01背包是逆序遍历v,而完全背包是顺序遍历的原因
    完全背包二维状态方程:
    令dp[i][v] 表示前i件物品恰好装入容量为v的背包中所能获得的最大价值
    dp[i][v] = max{ dp[i-1][v] , dp[i][v-w[i]]+c[i]} (1<=i<=n, w[i]<=v<=V) ,边界:dp[0][v]=0(0<=v<=V)

  3. 所以完全背包化简为一维后,dp[v]是上一状态的dp[i-1][v], dp[v-w[i]]是此时状态的dp[i][v-w[i]],故顺序遍历
    01背包中dp[v]、dp[v-w[i]]都是上一状态的,故逆序遍历
    (否则遍历到dp[j]时dp[0~j-1]都是此时的状态,上一状态已经被覆盖)

2️⃣ 恰好装满 🆚 无需完全装满

  1. 要求恰好装满背包,那么在初始化时除 dp[0]为0其它f[1..V]均设为-∞,这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解
  2. 如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将 dp[0..V]全部设为0。
  3. Why?可以这样理解——
    • 初始化的dp数组事实上就是前0件物品可以放入背包时的合法状态。
    • 如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的情况下被“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。
    • 如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了

💡这个小技巧完全可以推广到其它类型的背包问题

题目解析

这道题是恰好装满的完全背包问题,每种货币可选多次,要组成货币面值为V

1️⃣ 设置dp[i][v] 表示前i种货币恰好组成面值v的方案,为👇两种方案数相加

  • 当前不选第i种货币,则为前 i-1 种货币组成面值v的方案
  • 当前选第i中货币,则为前i种货币组成面值 v-w[i] 的方案(w[i]存储第 i 种货币的面值)

​ dp[i][v] = dp[i-1][v] + dp[i][v-w[i]] (1<=i<=n, w[i]<=v<=V)

2️⃣ 因为最终要恰好组成面值V,所以是“恰好完全装满”问题,这时dp[0][0]用前0种货币恰好组成面值0的方案数目为1
(只有0面值能被前0种货币构成这一种合法解)其他dp[0][v]=0,表示都没有合法解,方案数是0

3️⃣ 化简为一维动态转移方程:dp[v] = dp[v] + dp[v-w[i]] (w[i]<=v<=V)
因为是完全背包,所以顺序遍历 v,边界dp[v]=0 (0<=v<=V), dp[0]=1

代码

#include <cstdio>
#include <algorithm>

#define maxn 30
#define maxv 10005
typedef long long LL;
using namespace std;

int main() {
    int n, V;
    int w[maxn];
    LL dp[maxv];
    while (scanf("%d%d", &n, &V) != EOF) {
        for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
        fill(dp, dp + maxv, 0);
        dp[0] = 1;
        for (int i = 1; i <= n; i++) {
            for (int v = w[i]; v <= V; v++) {
                dp[v] = dp[v] + dp[v - w[i]];
            }
        }
        printf("%lld\n", dp[V]);
    }
    return 0;
}

本文作者:Joey-Wang

本文链接:https://www.cnblogs.com/joey-wang/p/14541196.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Joey-Wang  阅读(69)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开