[笔记]动态规划与01背包问题


此笔记为个人学习记录,欢迎大佬指出误区,如有和你的认知矛盾之处,以你为准

关于动态规划

基本思想:避免重复计算

适用问题特征:重复子问题

如何实现:将原问题拆分为若干个更小的问题,通过dp数组存储子问题的结果,
(想象一个金字塔,先把最底层的盖完后,第二层就能在最底层的基础上再盖,以此类推),
在某个较大子问题包含已经求解过的子问题的时候,直接从dp数组中取得该结果,得出结果后再次存入dp数组,以避免该子问题的重复计算,
以此类推,当某个更大的子问题包含这个较大子问题时,同样可以直接从dp数组中再次取得结果,直到问题解决。

思考过程

显然动态规划的灵魂所在就是dp数组(Dynamic Programming)
所以首先得明确dp数组的下标(还有要使用一维还是更多维的数组)和其对应的子问题的含义
然后确定递推公式:即上述中子问题较大子问题更大的子问题之间是如何转换的(这一步一般是最难也是最关键的
之后将dp数组初始化,确定遍历顺序,找几个例子检验一下即可

01背包问题

概述

有N件物品和一个容量为W的背包。
每第i件物品的重量为weight[i],价值为value[i]。
每件物品仅有一件,可选择装与不装。
求解选择装入哪些物品时得到的价值最大。

思考过程

建立dp数组://存放子问题的结果

对dp[i][j]:
i:[0…i]号备选物品
注意这里是从0号到i号之间的所有物品
j:背包的容量上限
时刻注意dp[i][j]数组仅存放了一个数字,这个数字对应着条件为i和j时的最优解

递推公式推导://建立i与i-1之间的关系

首先固定背包容量为j,对每一个第i件物品有两种选择:
1>不放入这个第[i]件物品:
此时有 dp[i][j]=dp[i-1][j] //相当于不存在这一件物品(这一件物品不影响问题的最优解)
所以结果为0~i-1(没有第i件物品)时的最优解
2>放入这个第i件物品时:
dp[i][j]=+value[i]+dp[i-1][j-weight(i)] //优先处理这一件物品,获得价值value[i],同时背包容量减少这件物品的重量即[j-weight(i)]
此时背包大概率还是能装下一点别的东西的,这点剩下的容量能装下的价值就是dp[i-1][j-weight(i)](因为第i号物品已经在包里了,所以备选物品从0~i-1里选)

那么到底是装还是不装呢,只需要对上面两种情况带来的价值进行比较即可,于是得到递推公式
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

dp数组初始化

显然备选物品数量和背包容量不应该为负值,但是由递推公式可以预料到,i=0时,i-1就为负值,所以在i=0时,不应依照递推公式,而是应当自己手动对其进行赋值,以便后续的问题依次进行解决

对dp[0][j]:此时备选物品仅有第0号(也就是第一件,这里标为0只是为了对应数组规则)物品,此时对与容量为j的背包有两种可能:
当背包容量装不下第0号物品时,即j<weight(0)时,dp[0][j]=0;
当背包能装下第0号物品时,即j>=weight(0)时,dp[0][j]=value(0);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我明白了。关于分支限界法的01背包问题,我可以给你一些思路和算法实现的笔记。 首先,01背包问题是指在有限的背包容量下,从给定的一组物品中选取一些物品放入背包中,使得背包中物品的总价值最大。而分支限界法则是一种解决优化问题的方法,它通过限制问题的搜索空间,逐步缩小问题的解空间,从而获得最优解。 对于01背包问题,我们可以采用以下步骤: 1. 构造一个搜索树,根节点代表空背包,每个节点代表向背包中添加一个物品或不添加物品的决策。 2. 对于每个节点,计算当前背包中物品的总价值以及还有多少容量可以使用。 3. 对于每个未被扩展的节点,计算它的上界价值(即背包在剩余容量中所能获得的最大价值),并按照上界价值从大到小排序。 4. 选择上界价值最大的节点进行扩展,即添加一个物品或不添加物品的两种决策分别扩展出两个子节点。 5. 不断重复步骤3和4,直到搜索树中的所有节点都被扩展完毕。 以下是一个简单的C语言实现,其中用一个结构体表示每个节点的状态,通过一个优先队列来实现按照上界价值排序的功能: ```c #include <stdio.h> #include <stdlib.h> #define MAX_N 1000 int n, c; int w[MAX_N], v[MAX_N]; int max_v; typedef struct Node { int level; int value; int weight; int bound; } Node; typedef struct PriorityQueue { Node* arr[MAX_N]; int size; } PriorityQueue; PriorityQueue* createPriorityQueue() { PriorityQueue* q = (PriorityQueue*)malloc(sizeof(PriorityQueue)); q->size = 0; return q; } void push(PriorityQueue* q, Node* node) { q->arr[q->size++] = node; int i = q->size - 1; while (i > 0 && q->arr[i]->bound > q->arr[(i - 1) / 2]->bound) { Node* temp = q->arr[i]; q->arr[i] = q->arr[(i - 1) / 2]; q->arr[(i - 1) / 2] = temp; i = (i - 1) / 2; } } Node* pop(PriorityQueue* q) { Node* node = q->arr[0]; q->arr[0] = q->arr[--q->size]; int i = 0; while (i * 2 + 1 < q->size) { int j = i * 2 + 1; if (j + 1 < q->size && q->arr[j + 1]->bound > q->arr[j]->bound) { j++; } if (q->arr[i]->bound < q->arr[j]->bound) { Node* temp = q->arr[i]; q->arr[i] = q->arr[j]; q->arr[j] = temp; i = j; } else { break; } } return node; } void solve() { PriorityQueue* q = createPriorityQueue(); Node* root = (Node*)malloc(sizeof(Node)); root->level = 0; root->value = 0; root->weight = 0; root->bound = max_v; push(q, root); while (q->size > 0) { Node* node = pop(q); if (node->bound <= max_v || node->level == n) { continue; } Node* left = (Node*)malloc(sizeof(Node)); left->level = node->level + 1; left->value = node->value + v[left->level]; left->weight = node->weight + w[left->level]; if (left->weight <= c) { left->bound = left->value + (max_v - left->value) * 1.0 / w[left->level]; if (left->value > max_v) { max_v = left->value; } push(q, left); } Node* right = (Node*)malloc(sizeof(Node)); right->level = node->level + 1; right->value = node->value; right->weight = node->weight; right->bound = right->value + (max_v - right->value) * 1.0 / w[right->level]; push(q, right); } } int main() { scanf("%d%d", &n, &c); for (int i = 1; i <= n; i++) { scanf("%d%d", &w[i], &v[i]); max_v += v[i]; } solve(); printf("%d", max_v); return 0; } ``` 希望这些笔记能够对你有所帮助,如果有任何问题,欢迎随时问我。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值