WP上动态规划的的适用情况如下。
- 最优子结构性质。如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质(即满足最优化原理)。最优子结构性质为动态规划算法解决问题提供了重要线索。
- 无后效性。即子问题的解一旦确定,就不再改变,不受在这之后、包含它的更大的问题的求解决策影响。
- 子问题重叠性质。子问题重叠性质是指在用递归算法自顶向下对问题进行求解时,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只计算一次,然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只是在表格中简单地查看一下结果,从而获得较高的效率。
01背包解题思路大致就是:
给定这些物品的重量(用数组[]weight表示)和价值(用数组[]value表示),背包的最大能装的重量为w,最多能装物品的数量为n。
设一个数组Max[i][j] 其实i是选择了前面的多少件物品,j就是当前背包的剩余容量。Max[i][j]的值就是当前最大价值
Max数组的大小其实可以确定,int Max[][] = new int[n+1][w+1],因为循环是从1开始的,防止角标越界。
递归出口是什么呢?
当递归到背包不能再装东西的情况,也就是Max[i][0] = 0;
当递归到背包只能装0件物品时,也就Max[0][j] = 0;
再推算出状态转移方程。一共就两个状态的:
容量放不下,直接不拿:Max[i][j] = Max[i-1][j],也就是不拿当前的i物品,价值和当前背包容量不变,和拿了第i-1个物品的价值重量一样。
容量放的下。拿的话就分两种情况。要比较谁大谁小。
一个是不拿:Max[i][j] = Max[i-1][j]。
一个拿:Max[i][j] = Max[i-1][j-weight[i-1]] + value[i-1].请注意这里的weight[i-1]和value[i-1]
import java.util.Scanner;
class Knapsack01{
public static void main(String []args){
Scanner sc = new Scanner(System.in);
int w = sc.nextInt(); //背包的最大能装多少重量
int n = sc.nextInt(); //背包最多能装物品的数量
int []weigth = {2,2,6,5,4};//物品的重量
int []value = {6,3,5,4,6};// 对应物品价值
DP(w,n,weigth,value);
}
public static void DP(int w,int n,int []weigth,int []value){
int [][]Max = new int [n+1][w+1];
for(int i=0;i<=n;i++)
Max[i][0] = 0;//背包不能再装东西的情况,递归出口
for(int j=0;j<=w;j++)
Max[0][j] = 0;//已经到了前0件物品的选择,也是递归出口
//i为前n件物品,j为背包的当前容量
for(int i=1;i<=n;i++){
for(int j=1;j<=w;j++){
if(weigth[i-1] <= j)
Max[i][j] = Math.max(Max[i-1][j],Max[i-1][j-weigth[i-1]]+value[i-1]);
//weigth数组和value数组是从0开始的,而Max[i][j]数组中,j是从1开始的
//要搞清楚当前的物品是什么。
else
Max[i][j] = Max[i-1][j];
}
}
for(int i=0;i<Max.length;i++){
for(int j=0;j<Max[i].length;j++){
System.out.print(Max[i][j]+" ");
}
System.out.println("");
}
}
}
如果觉得weight[i-1]和value[i-1]看不不舒服,可以让weight数组和value从角标1开始赋值,然后再修改状态转移方程中的角标。
以上。
参考:https://www.cnblogs.com/lfeng1205/p/5981198.html