01背包问题

文章介绍了01背包问题,这是一个使用动态规划解决的经典优化问题。通过建立状态转移方程,找出在有限容量的背包中选取物品以获得最大价值的策略。代码示例展示了如何实现这一算法,最终得出dp[n][m]为最大价值。同时,文章提供了回溯方法来找出最优解的商品组合。
摘要由CSDN通过智能技术生成

问题描述

 

01背包问题是动态规划中的一个经典问题。它的本质是一个集合中选择若干元素,使得这些元素满足某种特定条件并且具有最大的价值。在背包问题中,我们要把一些物品(每个物品有自己的价值和体积)装入一个容量为m的背包中,问怎样装才能使背包的内物价值最大。这是一个经典的优化问题,可以通过动态规划来解决。

假设有n个物品和一个容量为m的背包。第i个物品的体积是Wi,价值是Vi。那么,最优解就是如下所述:在不超过背包容量的前提下,选出一些物品使得它们的总价值最大

问题思路

解决这个问题的方法是采用动态规划思想。令dp[i][j]表示将前i个物品放入一个容量为j的背包中所得到的最大价值。则有如下状态转移方程:

dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]);

初始化:

对dp[i][j]而言,i或j为0时,价值都是0

递推关系式:

  • j<w(i)      V(i,j)=V(i-1,j)
  • j>=w(i)     V(i,j)=max{V(i-1,j),V(i-1,j-w(i))+v(i)}

第一个式子表示不选择第i个物品,第二个式子表示选择第i个物品。在选择第i个物品的情况下,背包容量要减去Wi,因此总价值要加上Vi。

最终结果

最终的答案就是dp[n][m],表示将n个物品放入一个容量为m的背包中所得到的最大价值。

代码实现:

#include <iostream>
#include <algorithm>
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    int w[n+1], v[n+1];
    for (int i = 1; i <= n; i++) {
        cin >> w[i] >> v[i];
    }
    int dp[n+1][m+1];
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            if (i == 0 || j == 0) {
                dp[i][j] = 0;
            } else if (j < w[i]) {
                dp[i][j] = dp[i-1][j];
            } else {
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]);
            }
        }
    }
    cout << dp[n][m] << endl;
    return 0;
}

最终的答案就是dp[n][m],表示将n个物品放入一个容量为m的背包中所得到的最大价值。

最优解由哪些商品组成?

我们不妨看看上面是如何通过递推关系式求得了最优解:

V(i,j)=V(i-1,j)  => 说明没有选择第i 个商品,则回到V(i-1,j)
V(i,j)=V(i-1,j-w(i))+v(i) => 说明装了第i个商品,它是最优解组成的一部分,然后我们回到装这个商品之前的状态,即V(i-1,j-w(i))

一直遍历到i=0为止,找到了所有解的组成。


void find(int i, int j) { //最优解情况
	if (i >= 0) {
		if (dp[i][j] == dp[i - 1][j]) {
			flag[i] = 0;//标记这个物品的编号为0,代表没有装入它
			find(i - 1, j);
		}
		else if (j - w[i] >= 0 && dp[i][j] == dp[i - 1][j - w[i]] + v[i]) {
			flag[i] = 1;
			find(i - 1, j - w[i]);//标记这个物品的编号为1,代表装入了它
		}
	}
}

总之,01背包问题是动态规划中一个非常经典的问题。通过状态转移方程,我们可以得到dp数组。在代码实现中,我们需要注意一些边界问题,比如数组下标的起点等等。选择好状态转移方程,就可以利用动态规划的思想来解决这个问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值