关于dp数组大小,边界,循环上线,由于这几个值在代码化的时候是有关联的,一开始会以为有点不清不楚的,可是这个问题自己只要理清楚一次就不会再有问题了。java
两种方式都是能够的,这里建议使用dp[n+1][w+1]的方式创建数组,数组
有如下几个好处:spa
1.动态的数值不用加一减一(dp数组)get
2.循环上限直接采用限制条件n和w就能够了,循环开始更符合现实含义循环
众所周知01背包问题由如下部分构成:static
n选择的数量,01背包中为物品的数量,co
w开销限制,01背包中为背包大小,new
g数组,每一个选择的价值,return
p数组,每一个选择的开销。背包问题
dp[n][w]表示在n个选择的状况和w个开销限制的状况下,最优解。
动态转移方程为dp[n][w]=max(dp[n-1][w],dp[n-1][w-p[i]]+g[i]),表示dp[n][w]的最优解,为不选择ni时候的最优解,和选择ni时候的最优解中的较优解组成。i表示有没有第i个选择。
dp[n+1][w+1]演示:
public static int[][] getMostGold2(int n, int w ,int[] g,int[] p){
int[][] dp = new int[n+1][w+1];
// for (int j = 0; j <=w; j++) {
// dp[0][j]=0;
// }
for (int i = 1; i <=n; i++) {
for (int j = 1; j <=w ; j++) {
if(j>=p[i-1])
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-p[i-1]]+g[i-1]);
else
dp[i][j]=dp[i-1][j];
}
}
return dp;
固定边界的时候,不须要额外固定,由于java中数组初始为0,循环i和j都从1开始,更符合现实,从一个选择和一个限制开始,为0其实没有实际意义。
由于数组长度是n+1和w+1,因此循环上线直接n和w。循环中涉及到的p[i]和g[i]都要-1,i是实际的i,比数组p和g中的大1。
输出整个结果的时候i和j也都是从1开始,dp[i+1][j+1]是最优解。
dp[n][w]演示:
public static int[][] getMostGold2(int n, int w ,int[] g,int[] p){
int[][] dp = new int[n][w];
for (int j = 0; j <w; j++) {
if(j+1>=p[0])
dp[0][j]=g[0];
else
dp[0][j]=0;
}
for (int i = 1; i <n; i++) {
for (int j = 0; j < w; j++) {
if (j +1 >= p[i] && (j - p[i])>=0) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - p[i]] + g[i]);
}
else
dp[i][j] = dp[i - 1][j];
}
}
return dp;
}
dp[n][w]的实现就会麻烦一些,首先须要计算边界,即为第一行,有一个选择的时候,没有边界不会终止。
并且由于j+1才是实际上j表达的数量现实,因此和p[i]比较的时候要+1,而且还要保持(j - p[i])>=0),否则j+1=p[i]的时候,dp[i-1][-1]的状况,这个是最麻烦的!!
其余一些状况dp[n][w+1]和dp[n+1][w]就不演示了,逗比dp[n][w]好,可是没有dp[n+1][w+1]明朗!
n+1和n的区别就在因而否须要首先计算边界,w和w+1实际意义上转换比较绕,建议不要用w,尽可能用w+1,这样第一列为空,后面的列号和实际限制数量数值上是一致的,便于理解!