与01不同的点只有一个,就是允许物品多次放入
首先分析这个问题得到状态转移方程 dp[i][j]=max(dp[i-1][j],dp[i-1][j-k*w[i]]+v[i]*k);
这是最朴素的方法
实现代码:
for(i=1;i<=n;i++) { for(j=1;j<=V;j++) { for(k=0;k <= j/w[i-1];k++) { dp[i][j] = max_2(dp[i][j], dp[i-1][ j-k*w[i-1] ] + k*p[i-1]); } } }
对于取多个物品的情况 可以写出它的状态转移方程:
dp[i][j]=dp[i][j-w[i]]+v[i] 可以用递归实现
又考虑到01背包时从后向前是为了避免覆盖前一个dp[i-1]的值 那么如果我从前向后就可以得到dp[i][j]=dp[i][k]+v[i] 这种情况
所以完全背包滚掉一维是这种样子:
for(i=1;i<=n;i++) { for(j=1;j<=V;j++) { for(k=0;k <= j/w[i-1];k++) { dp[i][j] = max_2(dp[i][j], dp[i-1][ j-k*w[i-1] ] + k*p[i-1]); } } }
ac代码:
#include<bits/stdc++.h> using namespace std; const int MAX=500005; const int INF=0x3f3f3f3f; int dp[MAX]; int main (){ int t; cin>>t; while(t--){ int n,m; cin>>n>>m; m=m-n; cin>>n; int coin_w[505],coin_v[505]; for(int i=1;i<=n;i++) cin>>coin_v[i]>>coin_w[i]; memset(dp,INF,sizeof(dp)); dp[0]=0; for(int i=1;i<=n;i++) for(int j=coin_w[i];j<=m;j++) dp[j]=min(dp[j],dp[j-coin_w[i]]+coin_v[i]); if(dp[m]==INF)cout<<"This is impossible."<<endl; else cout<<"The minimum amount of money in the piggy-bank is "<<dp[m]<<"."<<endl; } }