赞
踩
题目描述:
有n件物品 ,每件物品重w[i],价值v[i],
现在需要选出若干件物品放入到一个容量为V的背包中,
当选入物品体积不超过 V时,求最大价值。
输入格式:
第一行两个整数,n,m,用空格隔开,分别表示物品数量和背包容积。接下来有 n 行,每行两个整数 wi,vi,用空格隔开,分别表示第 i 件物品的体积和价值。
输入样例:
- 4 5
- 1 2
- 2 4
- 3 4
- 4 5
输出样例:
8
数据范围:
0<n,m≤1000 0<vi,wi≤1000
对于上面的背包问题我有两种解法分别是动态规划和深度优先搜索。
这是一道典型的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]);
代码如下
#include<iostream> #define N 1010 using namespace std; int w[N],v[N],dp[N][N]; int main(){ int n,m; cin>>n>>m;//物品的个数以及背包的容量 for(int i=1;i<=n;i++){ cin>>w[i]>>v[i];//输入物品的体积以及价值 } for(int i=0;i<=n;i++)//当背包容量为0时,价值为0 dp[i][0]=0; for(int i=0;i<=m;i++)//当物品个数为0时,背包的容积不论是多少其存放物品的最大价值为0 dp[0][i]=0; for(int i=1;i<=n;i++){//i表示物品的个数 for(int j=1;j<=m;j++){//j表示背包容积 if(j>=w[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);//状态转移方程 else dp[i][j]=dp[i-1][j]; } } cout<<dp[n][m];//输出n个物品放在容积为m的背包中的最大价值 return 0; }
深度优先搜索是指沿着树的某一个分支一直走,直至结尾再回溯,是图遍历算法的一种,用一句话概括就是:“一直往下走,走不通回头,换条路再走,直到无路可走”。
深度优先搜索的基础是递归。
分析:
首先要设置一个数组并初始化为0,这个数组用来表示n个物品是否被访问过,如果被访问则跳过该物品,否则访问该物品,访问过后要赋值为1;
当然要实现上面的步骤需要用到递归,在递归过程中通过背包的容量来计算背包中物品物品的价值,每当生成一个价值,就需要和之前的价值进行比较并且取最大价值,递归结束的条件是如果物品的体积之和大于背包所能容纳的最大体积则结束递归。
#include<iostream> #define N 1010 using namespace std; int w[N],v[N],str[N]={0}; int n,m,maxv=0,sum=0,sumv=0;//sum表示放在背包的体积,sumv表示在背包中物品的价值之和,maxv表示背包中的最大价值 void dfs(){ if(sum>m)//递归结束的条件,当物品的体积之和大于背包容量时,递归结束 return ; if(sumv>maxv)//当当前背包的物品的价值大于最大价值则更新maxv maxv=sumv; for(int i=0;i<n;i++){ if(!str[i]){//判断物品是否被访问 str[i]=1;//把访问的物品赋值为1 sum=sum+w[i];//加上该物品的体积 sumv=sumv+v[i]; dfs(); str[i]=0;//递归结束,说明加上该物品的体积时体积之和大于背包容量,把物品重新赋值为0; sum=sum-w[i]; sumv=sumv-v[i]; } } } int main(){ cin>>n>>m;//物品的个数以及背包容量 for(int i=0;i<n;i++) cin>>w[i]>>v[i]; dfs(); cout<<maxv; return 0; }
以上就是关于背包问题的两种解法。
可以通过以下题目来练习
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。