背包问题的两种解法

题目描述:

有n件物品 ,每件物品重w[i],价值v[i],

现在需要选出若干件物品放入到一个容量为V的背包中,

当选入物品体积不超过 V时,求最大价值。

输入格式:

第一行两个整数,n,m,用空格隔开,分别表示物品数量和背包容积。接下来有 n 行,每行两个整数 wi,vi,用空格隔开,分别表示第 i 件物品的体积和价值。

输入样例:

输出样例:

数据范围:

对于上面的背包问题我有两种解法分别是动态规划和深度优先搜索。

这是一道典型的01背包问题。首先我来说一下01背包和完全背包的区别:01背包的每件物品只能用一次,完全背包则是每件物品可以重复使用,没有次数限制。

由于这道题是01背包问题,所以主要来说一下01背包。利用动态规划,其目的就是将原问题分解成几个子问题,通过求解简单的子问题,把原问题给解决,和分治法类似。

动态规划,无非就是利用历史记录,来避免我们的重复计算。而这些历史记录,我们得需要一些变量来保存,一般是用一维数组或者二维数组来保存。

动态规划有三个步骤
1、定义数元素的含义
2、找出数组元素之间的关系式,例如斐波那契数列方程:f[i]=f[i-1]+f[i-2];这就是典型的数组元素之间的关系式
3、找出初始值,有了初始值再根据数组元素之间的关系式就能计算出剩余数组元素的值

动态规划的核心就是找到原问题与子问题的关系,并列出动态转移方程。

分析:

根据上面的问题我们可以定义一个二维数组dp[i][j]表示前i个物品放在容量为j的背包时的最大价值。对于第i个物品可以选也可以不选。

如果背包的容量j小于第i个物品的重量w[i]时,则前i个物品在容量为j的背包的最大价值等于前i-1个物品在容量为j的背包中的最大价值,即dp[i][j]=dp[i-1][j];
如果背包的容量j小于第i个物品的重量w[i]时,则前i个物品在容量为j的背包的最大价值等于前i-1个物品在容量为j的背包中的最大价值与第i个物品放在容量为j的背包中的价值加上剩余i-1个物品放在容量为j-w[i]背包中的价值之和的最大值,即dp[i][j]=max(dp[i-1][j],v[i]+dp[i-1][j-w[i]);

由此可知该题的状态转移方程是dp[i][j]=max(dp[i-1][j],v[i]+dp[i-1][j-w[i]);

代码如下

深度优先搜索是指沿着树的某一个分支一直走,直至结尾再回溯,是图遍历算法的一种,用一句话概括就是:“一直往下走,走不通回头,换条路再走,直到无路可走”。

深度优先搜索的基础是递归。

分析:

首先要设置一个数组并初始化为0,这个数组用来表示n个物品是否被访问过,如果被访问则跳过该物品,否则访问该物品,访问过后要赋值为1;

当然要实现上面的步骤需要用到递归,在递归过程中通过背包的容量来计算背包中物品物品的价值,每当生成一个价值,就需要和之前的价值进行比较并且取最大价值,递归结束的条件是如果物品的体积之和大于背包所能容纳的最大体积则结束递归。

以上就是关于背包问题的两种解法。

可以通过以下题目来练习

完全背包问题

多重背包

分组背包

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我来为您介绍一下。 1.暴力递归法 这种方法是通过递归的方式来解决01背包问题的。对于每个物品,我们可以选择将其放入背包或者不放入背包,然后再考虑下一个物品。当考虑完所有的物品后,我们就可以得到一个解,然后再从所有的解中选择一个最优解。这种方法的时间复杂度为O(2^n),其中n为物品的个数,因为对于每个物品,我们都有两种选择。 下面是使用暴力递归法实现01背包问题的代码: ```c #include <stdio.h> int max(int a, int b) { return a > b ? a : b; } int knapsack(int W, int wt[], int val[], int n) { if (n == 0 || W == 0) { return 0; } if (wt[n-1] > W) { return knapsack(W, wt, val, n-1); } return max(val[n-1] + knapsack(W-wt[n-1], wt, val, n-1), knapsack(W, wt, val, n-1)); } int main() { int val[] = {60, 100, 120}; int wt[] = {10, 20, 30}; int W = 50; int n = sizeof(val)/sizeof(val[0]); printf("Maximum value: %d", knapsack(W, wt, val, n)); return 0; } ``` 2.动态规划法 动态规划法是通过构建一个二维数组来解决01背包问题的。我们可以将物品放入背包中,或者不将其放入背包中。我们可以将二维数组中的每个元素看做是一个子问题的解,然后我们可以通过递推的方式来求解整个问题的解。此方法的时间复杂度为O(nW),其中n为物品的个数,W为背包的容量。 下面是使用动态规划法实现01背包问题的代码: ```c #include <stdio.h> int max(int a, int b) { return a > b ? a : b; } int knapsack(int W, int wt[], int val[], int n) { int dp[n+1][W+1]; for (int i = 0; i <= n; i++) { for (int w = 0; w <= W; w++) { if (i == 0 || w == 0) { dp[i][w] = 0; } else if (wt[i-1] > w) { dp[i][w] = dp[i-1][w]; } else { dp[i][w] = max(val[i-1] + dp[i-1][w-wt[i-1]], dp[i-1][w]); } } } return dp[n][W]; } int main() { int val[] = {60, 100, 120}; int wt[] = {10, 20, 30}; int W = 50; int n = sizeof(val)/sizeof(val[0]); printf("Maximum value: %d", knapsack(W, wt, val, n)); return 0; } ``` 3.回溯法 回溯法是通过搜索所有可能的解来解决01背包问题的。在搜索的过程中,我们可以将物品放入背包中,或者不将其放入背包中。如果当前的背包容量已经超过了背包的容量,或者已经考虑完所有的物品,那么就可以得到一个解。然后再从所有的解中选择一个最优解。这种方法的时间复杂度为O(2^n),其中n为物品的个数。 下面是使用回溯法实现01背包问题的代码: ```c #include <stdio.h> int max(int a, int b) { return a > b ? a : b; } void knapsack(int W, int wt[], int val[], int n, int curW, int curVal, int *maxVal) { if (curW > W) { return; } if (n == 0) { *maxVal = max(*maxVal, curVal); return; } knapsack(W, wt, val, n-1, curW, curVal, maxVal); knapsack(W, wt, val, n-1, curW+wt[n-1], curVal+val[n-1], maxVal); } int main() { int val[] = {60, 100, 120}; int wt[] = {10, 20, 30}; int W = 50; int n = sizeof(val)/sizeof(val[0]); int maxVal = 0; knapsack(W, wt, val, n, 0, 0, &maxVal); printf("Maximum value: %d", maxVal); return 0; } ``` 4.分支限界法 分支限界法是通过搜索所有可能的解来解决01背包问题的。在搜索的过程中,我们可以将物品放入背包中,或者不将其放入背包中。但是我们在搜索的过程中,可以使用一些策略来减少搜索的次数。例如,我们可以将物品按照单位重量的价值排序,然后先考虑价值高的物品。这种方法的时间复杂度取决于使用的策略,但是一般来说,它的时间复杂度可以达到O(nlogn)。 下面是使用分支限界法实现01背包问题的代码: ```c #include <stdio.h> #include <stdlib.h> typedef struct Item { int val; int wt; double vw; } Item; int cmp(const void *a, const void *b) { Item *ia = (Item *)a; Item *ib = (Item *)b; return ib->vw - ia->vw; } int bound(Item items[], int n, int W, int curW, int curVal) { if (curW > W) { return 0; } int boundVal = curVal; int j = n; int totW = curW; while ((j < n) && (totW + items[j].wt <= W)) { totW += items[j].wt; boundVal += items[j].val; j++; } if (j < n) { boundVal += (W - totW) * items[j].vw; } return boundVal; } void knapsack(Item items[], int n, int W, int *maxVal) { qsort(items, n, sizeof(Item), cmp); int curW = 0, curVal = 0; int i = 0; int boundVal = bound(items, n, W, curW, curVal); int Q[n]; Q[i] = 0; while (i >= 0) { int j = Q[i]; i--; if (j == n) { if (curVal > *maxVal) { *maxVal = curVal; } continue; } if (curW + items[j].wt <= W) { curW += items[j].wt; curVal += items[j].val; if (curVal > *maxVal) { *maxVal = curVal; } boundVal = bound(items, n, W, curW, curVal); if (boundVal > *maxVal) { i++; Q[i] = j+1; } curW -= items[j].wt; curVal -= items[j].val; } boundVal = bound(items, n, W, curW, curVal); if (boundVal > *maxVal) { i++; Q[i] = j+1; } } } int main() { Item items[] = {{60, 10, 6}, {100, 20, 5}, {120, 30, 4}}; int n = sizeof(items)/sizeof(items[0]); int W = 50; int maxVal = 0; knapsack(items, n, W, &maxVal); printf("Maximum value: %d", maxVal); return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值