问题描述:
有n件物品,每件物品的重量为w[i],其价值为v[i],现在有个容量为m的背包,如何选择物品使得转入背包物品的价值最大。
思路:
首先想到的肯定是枚举所有物品的排列组合,再从中找到价值最大的那个组合,时间复杂度O(n!)。同样可以通过选择是否需要某个物品来枚举出所有的排列组合,时间复杂度O(2^n)。这样的时间复杂度是完全不能接受的
采用动态规划的方法,时间复杂度只有O(nm),具体方法如下:
首先设置一个二维数组dp[][],令dp[i][j]表示前i个物品装进容量为j的背包能获得的最大价值,最后dp[n][m]的值就是问题的解
算法过程:
算法过程就是求解dp[n][m]的值的过程
①确定问题边界:显然当i = 0或j = 0时,dp[i][j] = 0,即dp[i][0] = 0;dp[0][j] = 0
②化解子问题:dp[i][j]表示前i个物品装进容量为j的背包能获得的最大价值
③状态转移方程:
对于容量为j的背包,考虑第i件物品,可将情况分为是否放入第i件物品两中情况:
1)如果不放入第i件物品,那么这个问题就转换为前i - 1个物品放入容量为j的背包的问题,即dp[i][j] = dp[i - 1][j]
2)如果放入第i件物品,那么当前背包的容量就变成了j - w[i],并使价值总量增加n[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])
实例:
题目描述:
北大网络实验室经常有活动需要叫外卖,但是每次叫外卖的报销经费的总额最大为C元,有N种菜可以点,经过长时间的点菜,网络实验室对于每种菜i都有一个量化的评价分数(表示这个菜可口程度),为Vi,每种菜的价格为Pi, 问如何选择各种菜,使得在报销额度范围内能使点到的菜的总评价分数最大。 注意:由于需要营养多样化,每种菜只能点一次。
输入描述:
输入的第一行有两个整数C(1<=C<=1000)和(1<=N<=100),C代表总共能报销的额度,N代表能点的菜数目。些下来的N行中,每行包括两个在1到100之间(包括1和100)的整数,分别标识菜的价格和菜的评价分数。
输出:
输出只包括一行,这一行只包含一个整数,表示在报销额度范围内,所点的菜得到的最大评价分数
样例输入:
90 4
20 25
30 20
40 50
10 18
样例输出:
95
代码:
#include <iostream>
#include <stdio.h>
using namespace std;
int main(){
int n, c;
while(scanf("%d %d", &c, &n) != EOF){
int w[n + 1];
int v[n + 1];
int dp[n + 1][c + 1];
for(int i = 1; i <= n; i ++){
scanf("%d %d", &w[i], &v[i]);
}
for(int i = 0; i <= n; i ++){
for(int j = 0; j <= c; j ++){
if(i == 0 || j == 0){
dp[i][j] = 0;
continue;
}
if(w[i] <= j){
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][c] << endl;
}
return 0;
}