背包问题(dp动态规划思路详解)
2017-11-29 17:31
429 查看
背包问题1---动态DP
题目:有n个重量和价值分别为wi,vi的物品。从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大的值。
输入:n = 4, (w, v) = {(2, 3), (1, 2), (3, 4), (2, 2)},W = 5
输出:W = 5
递归代码:
后来我们又发现明明数组就能搞定的为啥还要递归喃?所以直接用dp数组就可以解决问题了。
下面我们进行逆向循环规划:
题目:有n个重量和价值分别为wi,vi的物品。从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大的值。
输入:n = 4, (w, v) = {(2, 3), (1, 2), (3, 4), (2, 2)},W = 5
输出:W = 5
思路: 首先我们采用递归的想法。定义一个函数 int rec(int i, int j)。这个函数的作用是求得挑选的最佳效果。调用的时候分别传入0,W。 然后我们就要开始递归了,采用分治策略。 我们从0个物品开始向后思考是否要选取这个物品 int rec(int i, int j){ int res; if(i == n){如果这个物品已经超出范围了,因为i传入的时候是0,最后一个i应该是n-1 说明我们无法选择了: res = 0; }esle if(j < w[i]){ 如果当前这个物品的重量比我们还需要选的重量还要大,就选不了这个物品 选不了我们就跳过它选下一个: res = rec(i + 1, j); //这里喃我们需要一种抽象思想,就是通过rec函数就能 找出最佳的res。这个物品选不了了,我们从下一个 物品开始选出最佳答案。 }else { 可以选了,我们比较选它和不选它那个是更有价值的: res = max(rec(i + 1, j), rec(i + 1, j - w[i]) + v[i]); // rec(i + 1, j):同上,表示我们不选它,从下一个开始选出最佳解 //rec(i + 1, j - w[i]) + v[i]:我们知道调用rec(i + 1, j - w[i])选了本物品后剩下还要选的重量在下一个开始的 最 大价值 + 本物品价值,也就是总的rec(i, j)的最大价值 } }
递归代码:
/* 输入: 4 2 3 1 2 3 4 2 2 5 输出:7 */ #include<cstdio> #include<algorithm> using namespace std; int n, W; const int MAX_N = 100; int w[MAX_N], v[MAX_N]; //从第i个物品开始挑选总重量小于j的部分 int rec(int i, int j){ int res; if(i == n){ //已经没有剩余的物品了 res = 0; }else if(j < w[i]){ //无法挑选这个物品(判断下一个) res = rec(i + 1, j); }else{ //不挑选和挑选的两种情况分别尝试一下 res = max(rec(i + 1, j), rec(i + 1, j - w[i]) + v[i]); } return res;//返回挑选了价值 } void solve(){ printf("%d\n", rec(0, W)); } int main(){ while(scanf("%d", &n) != EOF){ for(int i = 0; i < n; i++){ scanf("%d%d", &w[i], &v[i]); } scanf("%d", &W); solve(); } }但是我们发现些时候递归是很重复的,所以我们定义一个数组dp来保存每次递归的结果,如果已经递归过,我们就直接返回结果
int dp[MAX_N + 1][MAX_W + 1];//记忆化数组 int rec(int i, int j){ if(dp[i][j] >= 0){ //已经计算过的话直接使用之前的结果 return dp[i][j]; } int res; if(i == n){ res = 0; }else if(j < w[i]){ res = rec(i + 1, j); }else{ res = max(rec(i + 1, j), rec(i + 1, j - w[i]) + v[i]); } //将结果记录在数组中 return dp[i][j] = res; } void solve(){ //用-1表示尚未计算过,初始化整个数组 //使用memset可以对1字节为单位进行填充。对1,0可进行高速初始化。不过无法初始化化为1 memset(dp, -1, sizeof(dp)); printf("%d\n", rec(0, W)); }
后来我们又发现明明数组就能搞定的为啥还要递归喃?所以直接用dp数组就可以解决问题了。
下面我们进行逆向循环规划:
int dp[MAX_N + 1][MAX_W + 1];//要都初始化成0 void solve(){ for(int i = n - 1; i >= 0; i--){//我们从最后一个物品开始选(逆向) for(int j = 0; j <= W; j++){ if(j < w[i]){//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]); } } } printf("%d\n", dp[0][W]); }下面我们进行正向循环规划:
void solve(){ for(int i = 0; i < n; i++){ for(int j = 0; j <= W; j++){ if(j < w[i]){ dp[i + 1][j] = dp[i][j]; }else{ dp[i + 1][j] = max(dp[i][j], dp[i][j - w[i]] + v[i]); } } } printf("%d\n", dp [W]); }其他的dp方式:
void solve(){ for(int i = 0; i < n; i ++){ for(int j = 0; j <=W; j++){ dp[i + 1][j] = max(dp[i + 1][j], dp[i][j]); if(j + w[i] <= W){ dp[i + 1][j + w[i]] = max(dp[i + 1][j + w[i]], dp[i][j] + v[i]); } } } printf("%d\n", dp [W]); }
相关文章推荐
- 01背包问题动态规划详解
- 程序设计实习动态规划练习 Charm Bracelet(0/1背包问题dp)
- 0/1背包问题动态规划详解
- 动态规划之背包问题详解
- 动态规划和背包dp问题
- 0/1背包问题动态规划详解
- 0-1背包问题动态规划详解
- 0/1背包问题动态规划详解之一
- 动态规划:背包问题(DP系列)
- 0/1背包问题动态规划详解
- 01背包问题动态规划详解(转载)
- 0/1背包问题动态规划详解
- leetcode 279. Perfect Squares 类似背包问题 + 很简单的动态规划DP解决
- 0-1背包问题;动态规划;时间复杂度O(n方);给出最大价值与解得情况;内有动态规划思路总结;
- 背包问题动态规划详解
- 01背包问题动态规划详解(转载)
- 0/1背包问题动态规划详解(转)
- HDU 2159 FATE (动态规划dp之二维完全背包问题)
- 0/1背包问题动态规划详解
- leetcode 322. Coin Change 类似背包问题 + 很简单的动态规划DP解决