问题描述:
有N件物品和一个容量为V的背包。第i件物品的重量是weight[i],价值是value[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。注意,与背包问题不同的是,每件物品只有整个装入和不装入两个选项,且每件物品只能装入一次。
算法描述:
使用f(i,v)表示将前i个物品选择性装入剩余容量为v的背包时能得到的最大价值。这里我们把第i件物品和前i-1件物品分开来看,第i件物品只有装和不装两种选择。
1.如果不装的话剩余容量不减少,价值也不变,因此在这种情况下f(i,v)=f(i-1,v)
2.如果装的话则容量减少,价值增加,因此在这种情况下f(i,v)=f(i-1,v-weight[i])+value[i]
从自顶向下的视角看,如果装了第i个物品的话在下一层就要求解前i-1件物品在剩余容量减少的情况下的最大价值,如果没有装则要求解前i-1件物品在剩余容量不变的情况下的最大价值。
因此对于第i件物品的装和不装问题我们只要取两者的最大值就可以了。所以可以得到状态转移方程:
f(i,v)=f(i-1,v) (v<weight[i],即剩余容量不足,不能放入物品)
f(i,v)=f(i-1,v-weight[i])+value[i] (v>=weight[i],即剩余容量充足)
由于每一行数据仅取决于上一行,所以可使用滚动数组进行优化
代码:
#include <iostream> #include <vector> #include <algorithm> using namespace std; //f[i][v]=max{ f[i-1][v],f[i-1][v-w[i]]+val[i] } //f[v]=max(f[v-weight[i]]+value[i],f[v]) int solve(int n,int capacity,vector<int> &weight,vector<int> &value) { if(n<0||capacity<=0) return 0; vector<int> f(capacity+1); for(int i=0;i<n;i++) { for(int v=capacity;v>0;v--) { if(v>=weight[i]) f[v]=max(f[v-weight[i]]+value[i],f[v]); } } return f[capacity]; } int main() { int n,capacity; cin>>n>>capacity; vector<int> weight(n); vector<int> value(n); for(int i=0;i<n;i++) { cin>>weight[i]>>value[i]; } int ans=solve(n,capacity,weight,value); cout<<ans<<endl; return 0; }