01揹包問題中二維數組大小爲dp[n][w]仍是dp[n+1][w+1]的問題

關於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,這樣第一列爲空,後面的列號和實際限制數量數值上是一致的,便於理解!

相關文章
相關標籤/搜索