0-1背包问题
问题描述:
n件物品,每件物品重量为w[i],价值为v[i],现有一个容量大小为m的背包,如何选择使得背包内物品价值最大
思路(动态规划):
-
设置二维数组dp[][],令dp[i][j]表示前i件物品容量为j的背包所能获得的最大价值,则dp[n][m]为所求解;
-
只考虑第i件物品,可将情况分为放入或者不放入两种:
2.1
对容量j的背包,不放入第i件物品,则问题转化为将前i-1个物品放入容量为j的背包的问题 ,即dp[i][j] = dp[i-1][j];2.2
对容量j的背包,若放入第i件物品,则当前背包容量就变成j-w[i],得到第i件物品的价值v[i],问题转为将前i-1件物品装入容量为j-w[i]的背包的问题,即dp[i][j] = dp[i-1][j-w[i]]+v[i];
故状态转移方程为:
dp[i][j] = max( dp[i-1][j], dp[i-1][j-w[i]]+v[i] ); -
对于边界情况,显然得出有 dp[i][0] = 0, dp[0][j] = 0;(0 <= i <= n; 0 <= j <= m);
-
观察状态转移方程,第i件物品的取值仅与第i-1件物品的选取有关,故可将二维数组优化为一维数组,并用如下状态转移方程:
dp[j] = max( dp[j], dp[[j-w[i]]+v[i] );
注意:
根据未优化的状态转移方程:dp[i][j]用到的是dp[i-1][…]的数据,因此我们在更新dp[j]的时候必须确保dp[j]以及dp[j-w[i]]是第i-1轮的数据,因此更新dp[j]时必须要逆序更新,即倒序遍历j的值;
代码:
#include <iostream>
using namespace std;
const int MAXN = 1001;
int dp[MAXN];//下标表示容量,内容表示价值;在不同题目中,指代内容可更改,思路最重要
int w[MAXN];//物品重量
int v[MAXN];//物品价值
int main()
{
int n,m;//n件物品,容量为m
while(cin >> n >> m)
{
for(int i=0; i<n; i++)//输入
{
cin >> w[i] >> v[i];
}
for(int i=0; i<=m; i++)//初始化dp数组,注意i<=m,要更新到m
{
dp[i] = 0;
}
for(int i=0; i<n; i++)//依次判断第i件物品
{
for(int j=m; j >= w[i]; j--)//逆序更新; j>=w[i]是为了保证 dp[j-w[i]]下标不为负
{
dp[j] = max(dp[j], dp[