01背包问题中二维数组大小为dp[n][w]还是dp[n+1][w+1]的问题

关于dp数组大小,边界,循环上线,因为这几个值在代码化的时候是有关联的,一开始会觉得有点不清不楚的,但是这个问题本身只要理清楚一次就不会再有问题了。

两种方式都是可以的,这里建议使用dp[n+1][w+1]的方式建立数组,

有以下几个好处:

1.动态的数值不用加一减一(dp数组)

2.循环上限直接采用限制条件n和w就可以了,循环开始更符合现实含义

 

众所周知01背包问题由以下部分构成:

n选择的数量,01背包中为物品的数量,

w开销限制,01背包中为背包大小,

g数组,每个选择的价值,

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,这样第一列为空,后面的列号和实际限制数量数值上是一致的,便于理解!

 

posted @ 2020-03-26 21:02  Cooper_Xia  阅读(737)  评论(0编辑  收藏  举报