0-1背包
背包容量C,物品代价为w[i] 价值为v[i],所有物品只可使用一次,求最大收益.
f(i,c) 代表使用物品 [0,…,i] 填充容量c所获得的最大收益
第i件物品可以选择用或者不用
1.不用: f(i,c) = f(i-1,c)
2.用: f(i,c) = f(i-1,c-w[i])+v[i]
和上一行的进行比较
因此有: f(i,c)=max( f(i,c) , f(i-1,c-w[i])+v[i] )
dp[j] 就代表使用"当前"的物品,填充容量j,所获得的最大收益.
"当前"就是外层循环遍历物品得到[0,…,i]
0-1背包问题需要 逆序计算 ,因为每个状态都和之前的状态相关
初始化问题:对于收益来说本身的初始值就是0,在构造dp时已经初始化了
/*
f(i,c)代表使用物品[0,...,i]填充容量c所获得的最大收益
第i件物品可以选择用或者不用
1.不用: f(i,c) = f(i-1,c)
2.用: f(i,c) = f(i-1,c-w[i])+v[i]
和上一行的进行比较
因此有:f(i,c)=max( f(i,c) , f(i-1,c-w[i])+v[i] )
dp[j]就代表使用"当前"的物品,填充容量j,所获得的最大收益.
"当前"就是外层循环遍历物品得到[0,...,i]
0-1背包问题需要逆序计算,因为每个状态都和之前的状态相关
初始化问题:对于收益来说本身的初始值就是0,在构造dp时已经初始化了
*/
int testFor01(vector<int>& v,vector<int>& w,int c)
{
vector<int> dp(c+1,0);
/*初始化一位数组:仅使用第一件物品时的状态变化过程*/
// for(int j=0; j<=c; ++j){
// dp[j] = j>=w[0]?v[0]:0;
// }
/*开始增大使用范围:*/
for(int i=0; i<n; ++i){
/*当前容量需要大于该物品,才能继续用*/
for(int j=c; j>=w[i]; --j){
dp[j] = max(dp[j], v[i]+dp[j-w[i]]);
}
}
return dp[c];
}
完全背包
背包容量C,物品代价为w[i] 价值为v[i],所有物品可无限使用,求最大收益.
f(i,c)代表使用物品[0,…,i]填充容量c所获得的最大收益
第i件物品可以选择用或者不用
1.不用: f(i,c) = f(i-1,c)
2.用: f(i,c) = max( f(i,c-k*w[i])+k*v[i] ) 0<=k<= c/w[i]
注意:这里和0-1背包的区别:
从左向右依次计算使用0,1,2,…c/w[i]个物品i所获的收益,
和本行的左边进行比较
因此有:f(i,c)=max( f(i,c) , f(i,c-w[i])+v[i] )
dp[j]就代表使用"当前"的物品,填充容量j,所获得的最大收益.
"当前"就是外层循环遍历物品得到[0,…,i]
完全背包问题需要**顺序计算**,顺序会覆盖以前的状态,达到选择多次的情况
/*
f(i,c)代表使用物品[0,...,i]填充容量c所获得的最大收益
第i件物品可以选择用或者不用
1.不用: f(i,c) = f(i-1,c)
2.用: f(i,c) = max( f(i,c-k*w[i])+k*v[i] ) 0<=k<=c/w[i]
注意:这里和0-1背包的区别:
从左向右依次计算使用0,1,2,..c/w[i]个物品i所获的收益,
和本行的左边进行比较
因此有:f(i,c)=max( f(i,c) , f(i,c-w[i])+v[i] )
dp[j]就代表使用"当前"的物品,填充容量j,所获得的最大收益.
"当前"就是外层循环遍历物品得到[0,...,i]
完全背包问题需要顺序计算,顺序会覆盖以前的状态,达到选择多次的情况
*/
int testForFull(vector<int>& v,vector<int>& w,int c)
{
/*完全背包:顺序计算*/
vector<int> dp(c+1,0);
for(int i=0; i<v.size(); ++i){
for(int j=w[i]; j<=c; ++j){
dp[j] = max(dp[j],v[i]+dp[j-w[i]]);
}
}
return dp[c];
}
多重背包
背包容量C,物品代价为w[i] 价值为v[i],所有物品均有个数限制,求最大收益.
/*
多重背包类似于完全背包,只是k的范围变小了
f(i,c)代表使用物品[0,...,i]填充容量c所获得的最大收益
第i件物品可以选择用或者不用
1.不用: f(i,c) = f(i-1,c)
2.用: f(i,c) = max( f(i,c-k*w[i])+k*v[i] ) 0<=k<=n[i]
优化:转化为0-1背包(加上二分法)
*/
int testForMulti(vector<int>& v,vector<int>& w,vector<int> n,int c)
{
/*新的背包,物品*/
vector<int> vArr, wArr;
/*拆分物品*/
for(int i=0; i<v.size(); ++i){
int k=1;
while(n[i]-k>0){
n[i]-=k;
vArr.push_back(k*v[i]);
wArr.push_back(k*w[i]);
k*=2;
}
/*该物品剩余的部分合并后插入*/
vArr.push_back(n[i]*v[i]);
wArr.push_back(n[i]*w[i]);
}
/*新的0-1背包问题*/
vector<int> dp(c+1,0);
for(int i=0; i<vArr.size(); ++i){
for(int j=c; j>=wArr[i]; --j){
dp[j] = max(dp[j], vArr[i]+dp[j-wArr[i]]);
}
}
return dp[c];
}
二维背包
背包体积A,重量B,
物品体积a[i],重量b[i],价值v[i],求最大收益.
具体顺序计算还是逆序计算取决于本质是"0-1"还是"完全"背包
/*
相比于0-1背包只是多了一维限制,只需要多加一层内循环即可
f(i,A,B)代表使用物品[0,...,i]填充容量A,B所获得的最大收益
第i件物品可以选择用或者不用
1.不用: f(i,A,B) = f(i-1,A,B)
2.用: f(i,A,B) = f(i-1,A-a[i],B-a[i])+v[i]
因此有:f(i,A,B)=max( f(i,A,B) , f(i-1,A-a[i],B-a[i])+v[i] )
dp[row][col]就代表使用"当前"的物品,填充容量row,col,所获得的最大收益.
*/
int testFor2D(int A,int B,vector<int>& a,vector<int>& b,vector<int>& v)
{
/*此处按照逆序计算*/
vector<vector<int>> dp(A+1,vector<int>(B+1,0));
for(int i=0; i<N; ++i){
for(int row=A; row>=a[i]; --row){
for(int col=B; col>=b[i]; --col){
dp[row][col]=max(dp[row][col],dp[row-a[i]][col-b[i]]+v[i]);
}
}
}
return dp[A][B];
}