- 0-1背包
给定背包容量w,一系列物品{weight, value},每件物品只能放一件,求背包中物品价值的最大值
求解方法:在前i-1个状态下的最大值/最小值的前提下,把第i个状态求出来
dp[1,2…i][w]:背包重量为w时能获取到的最大价值
对于第i件物品,有两种情况:
(1)不放入:则前i-1件可占w的重量
(2)放入:则前i-1件可占w-weight[I]的重量
理清思路后我们以leetcode416为例撸一遍代码
先从思路最清晰,但时间复杂度和空间复杂度都很大的方法开始:
public boolean canPartition(int[] nums) {
//先加一遍 如果是奇数 则不能被分割
int sum = 0;
for (int i = 0; i < nums.length; i++){
sum += nums[i];
}
if ((sum & 1) == 1)
return false;
//转化为重量为sum/2的背包问题
int target = sum >> 1;
int[][] dp = new int[nums.length][target+1];
for (int j = nums[0]; j <= target; j++){
dp[0][j] = nums[0];
}
//外层遍历物品,第i 件
for (int i = 1; i < nums.length; i++){
//内层遍历重量,weight
for (int j = 0; j <= target; j++){
if (nums[i] > j)
dp[i][j] = dp[i-1][j];
else
dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-nums[i]] + nums[i]);
}
}
if (dp[nums.length-1][target] == target)
return true;
return false;
}
然后再来学习一种一维数组就能解决的方法:
写博客真是逼着自己成长 其实我自己之前都没看过一维是咋写的~
public boolean canPartition2(int[] nums) {
//前面是一样的
int sum = 0;
for (int i = 0; i < nums.length; i++){
sum += nums[i];
}
if ((sum & 1) == 1)
return false;
int target = sum >> 1;
//dp[j]表示原数组是否可以取出若干个数字,使得和为j
boolean[] dp = new boolean[target + 1];
dp[0] = true;
//遍历数组
for (int i = 0; i < nums.length; i++)
//dp[nums[i]]和dp[j-nums[i]]都能凑出来,更新dp[j]
for (int j = target; j >= nums[i]; j--)
dp[j] = dp[j] || dp[j-nums[i]];
//终极目标:dp[target]
return dp[target];
}
是不是简洁了很多哇~
- 完全背包
与0-1背包的区别为:每种物品有无限件可用
所以就多了每种物品可以放多少件的参数k<=weight/wi呀
for (int i=0; i<n; i++) {
// 正序遍历; 0-1背包是逆序遍历
for (int j=w[i]; j<=m; j++) {
maxValue[j] = max(maxValue[j], maxValue[j-w[i]] + v[i]);
}
}
- 多重背包
跟完全背包十分类似,只不过人家给自己给件数k加了个限制:每种物品有n[i]件
?把同一种物品的K件变成K件不同的物品->变成0-1背包问题(我瞎说的,等改天再整理~)
什么各种背包问题,其实都是人家总结的细化的动态规划问题
我记得春招的时候就好好的在本本上整理了一遍,所以到底整理到哪里去了?