01背包问题详解

背包问题详解

01背包

1.1引入

01背包问题实例:
假如你有一个背包,可以装10千克物品,你现在可以选择携带:

  • 三明治:0.5千克,价值10元
  • 水果:1千克,价值15元
  • 帐篷:4千克,价值30元
  • 饮料:3千克,价值5元
  • 地毯:3.5千克,价值20元
    如果每样东西都最多只能带一件,如何选择才能让物品的总价值最大?

1.2为什么不能使用暴力

如果有四样物品,那么有\(2^4=16\)种可能性
如果有十样物品,那么有1024种可能性
但如果有100件物品,那么就会有\(1267650600228229401496703205376\)种可能性
你的代码就会成功的\(TLE\)

1.3动态规划的解决思路

动态规划就是用表格记录下子问题的最优解

举例:

请点击下方网址查看数据👇
https://img2024.cnblogs.com/blog/3799445/202607/3799445-20260720155007356-1339556381.png
(背包容量为10)

背包容量 0 1 2 3 4 5 6 7 8 9 10
物品0 0 0 0 0 0 0 0 0 0 0 0
物品1 0 0 1 1 1 1 1 1 1 1 1
物品2 0 0 1 2 3 4 4 4 4 4 4
物品3 0 0 1 3 5 5 6 8 8 9 9
物品4 0 0 1 3 5 5 6 9 9 10 12
物品5 0 0 1 3 5 5 8 9 9 11 13

最终的答案就是13。

1.4如何构建表格?

使用公式dp[i][w] = max(dp[i-1][w], dp[i-1][w-重量[i]] + 价值[i])将表格填完整,所有格子默认为0

1.5为什么这样做?

1. 问题可以拆碎

原问题是"n个物品装容量小于上线的物品",我们把它拆成"前1个装各种容量"、"前2个装各种容量"……每个小问题独立求解。

2. 小问题的答案能复用

算第i行时,直接抄上一行的结果就行。因为"前i个"的最优解,要么包含第i个,要么不包含——不包含时就和"前i-1个"完全一样。

3. 选第i个时,剩下的仍是已解决的子问题

如果决定要第i个,那就先腾出它的重量,剩下的容量去装前i-1个——这个子问题上一行已经算过了,直接查表取数加价值即可。

4. 每个格子只存最优值

表格不记录"选了哪些物品",只记录"最大价值是多少"。因为后续推导只需要这个数字,不需要具体组合。

1.6.1例题

P1048 [NOIP 2005 普及组] 采药

题目描述

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是辰辰,你能完成这个任务吗?

输入格式

第一行有 \(2\) 个整数 \(T\)\(1 \le T \le 1000\))和 \(M\)\(1 \le M \le 100\)),用一个空格隔开,\(T\) 代表总共能够用来采药的时间,\(M\) 代表山洞里的草药的数目。

接下来的 \(M\) 行每行包括两个在 \(1\)\(100\) 之间(包括 \(1\)\(100\))的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出格式

输出在规定的时间内可以采到的草药的最大总价值。

输入输出样例 #1
输入 #1
70 3
71 100
69 1
1 2

输出 #1
3
说明/提示

【数据范围】

  • 对于 \(30\%\) 的数据,\(M \le 10\)
  • 对于全部的数据,\(M \le 100\)

【题目来源】

NOIP 2005 普及组第三题

1.6.2解法

本题每种草药只有选和不选两种选择,所以是01背包

1.6.3样例分析

70 3
71 100
69 1
1 2

\(总时间 70,共 3 株草药\)
\(草药1:耗时71,价值100 → 采不了(71 > 70)\)
\(草药2:耗时69,价值1\)
\(草药3:耗时1,价值2\)
最优方案:\(采草药2 + 草药3 = 耗时70\),价值 \(1+2 = 3\)

1.6.4代码详解

点击此处查看代码
#include<bits/stdc++.h>//万能头文件
using namespace std;
int dp[1005][1005];//自动初始化为0
int main() {
    int m,n;cin>>m>>n;//输入,注意m和n的顺序
    int w,c;//w为重量,c为价值
    for(int i=1;i<=n;i++) {//枚举行
        cin>>w>>c;
        for(int j=0;j<=m;j++) {//枚举列
            dp[i][j]=dp[i-1][j];//默认为上一次此重量的价值
            if(j>=w) {//如果重量足够携带此物品
                dp[i][j]=max(dp[i][j],dp[i-1][j-w]+c);//状态转移方程,取最大值
            }
        }
    }
    cout<<dp[n][m];//输出答案
    return 0;
}

1.7推荐练习题目

posted @ 2026-07-20 16:28  _wyt001  阅读(132)  评论(0)    收藏  举报