方法:直接尋找遞推關係
第 件物品選 個
int dp[maxn+1][maxn+1];
void solve(){
for(int i=0;i<n;i++)
for(int j=0;j<=W;j++)
for(int k=0;k*w[i]<=j;k++)
dp[i+1][j]=max(dp[i+1][j],dp[i][j-k*w[i]]+k*v[i]);
cout<<dp[n][W];
}
k的循環最壞可能從0到W,所以算法時間複雜度爲
我們尋找這個算法中多餘的計算(已知結果的計算)
在dp[i+1][j]
的計算中選擇 個的情況,與在dp[i+1][j-w[i]]
的計算中選擇 個的情況是相同的,所以dp[i+1][j]
的遞推中 部分的計算已在dp[i+1][j-w[i]]
的計算中完成了。則可按照如下方式變形:
void solve(){
for(int i=0;i<n;i++)
for(int j=0;j<=W;j++){
if(i<w[i]) dp[i+1][j]=dp[i][j];
else dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i]);
}
cout<<dp[n][W];
}
時間複雜度爲 。完全揹包問題也可以通過不斷重複利用一個數組來實現:
int dp[maxn+1];
void solve(){
for(int i=0;i<n;i++)
for(int j=0;j<=W;j++)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
cout<<dp[W];
}
DP數組的再利用
還可能通過將兩個數組滾動使用來實現重複利用。例如
dp[i+1][j]=max(do[i][j],dp[i+1][j-w[i]]+v[i])
這一遞推式中,dp[i+1]
計算時只需要dp[i]
和dp[i+1]
,所以可以結合奇偶性寫成如下形式:
int dp[2][maxn+1];
void solve(){
for(int i=0;i<n;i++)
for(int j=W;j<=W;j++){
if(j<w[i]) dp[(i+1)&1][j]=dp[i&1][j];
else dp[(i+1)&1][j]=max(dp[i&1][j],dp[(i+1)&1][j-w[i]]+v[i]);
}
cout<<dp[n&1][W];
}