01背包二维数组动态规划
* 01背包问题
* 给你一个可装载重量为W的背包和N个物品,每个物品有重量和价值两个属性。
* 其中第i个物品的重量为wt[i],价值为val[i],
* 现在让你用这个背包装物品,最多能装的价值是多少?
这是二维数组的代码,N个物品为外循环,W容量为内循环
int packaging(int N,int W,int[] wt,int[] val){
int [][] dp = new int[N+1][W+1];
for (int i = 1;i<=N;i++){
for(int j = 1;j<=W;j++){
if(j<wt[i-1]){
dp[i][j] = dp[i-1][j];//容量不够了,只能不放当前物品
}else{
dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-wt[i-1]]+val[i-1]);//wt[i-1]和val[i-1]就是第i个数
//不放和放比较,取大的;
//放:N-1个物品,W减去当前物品重量的容量---此情况下的最大值+放了当前物品的值=dp[i][j]
}
}
}
return dp[N][W];
}
一维数组方法
其实二维数组的填充顺序就是一层一层的,其中每层都是从左往右。且每个dp[i][j]都是由上一层的某几个元素得来的,如下所示,红圈dp[4][8]由黄圈得来,即:如果第四个物品能够放入,即当前容积大于物品4的大小,dp[4][8]就是放入和不放入取最大值:4+6和11。
所以可以缩减dp[][]为一维,每一层重复使用,覆盖上一层的数即可。
所以在dp[]内部,其每一位的数字都是一个一个的被这一层覆盖的,未覆盖前,其值还是上一层的在j位的值。如下图所示,在计算dp[4][8]时,dp[]中的数值其实是黄圈中的数值。我们需要的值已经被当前层的覆盖了。
所以每一层覆盖时,应该从后往前。即内循环改为从后往前。如下图
此时计算dp[4][8]所需的dp[3][3]和dp[3][8]都未被覆盖,即dp[8] = max(dp[8],dp[3]);代码如下
int packaging_oneDim (int N,int W,int[] wt,int[] val){
int dp[] = new int[1+W];
for (int i = 1; i <= N; i++) {
for (int j = W; j >=1; j--) {
if(j<wt[i-1]){
dp[j] = dp[j];
}else {
dp[j] = Math.max(dp[j],dp[j-wt[i-1]]+val[i-1]);
}
}
System.out.println(Arrays.toString(dp));
}
return dp[W];
}
- 在j<wt[i-1]时,原地赋值,所以内循环可优化
for (int j = W; j >=1; j--) {
if(j>=wt[i-1]){
dp[j] = Math.max(dp[j],dp[j-wt[i-1]]+val[i-1]);
}
}
即:j<当前物品大小的就不用看了,肯定不能选,所以进一步优化
for (int j = W; j >=wt[i-1]; j--) {
dp[j] = Math.max(dp[j],dp[j-wt[i-1]]+val[i-1]);
}
要求恰好装满
int packaging_oneDim_exactly (int N,int W,int[] wt,int[] val){
int dp[] = new int[1+W];
for (int i = 1; i < dp.length; i++) {
dp[i] = Integer.MIN_VALUE;
}
for (int i = 1; i <= N; i++) {
for (int j = W; j >=1; j--) {
if(j<wt[i-1]){
dp[j] = dp[j];
}else {//大于表示当前可以放,需要后续再放
dp[j] = Math.max(dp[j],dp[j-wt[i-1]]+val[i-1]);
}
}
}
return dp[W]<0?-1:dp[W];
}
思路以及截图来自小码哥教育。