sdddf算法中的背包问题可以分为0-1背包问题和完全背包问题。
两者的核心不同在于决策的数量,0-1背包适用于选择某个物体时只能选择一次,而完全背包可以无限次地选择某个物体,这也就直接导致两者的核心式的不同:
0-1背包
int f[m][n];//m代表总共m个物体,n代表背包总体积
for(int i=1;i<=m;i++)
for(int j=V;j>=v[i];j--)
f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
完全背包
int f[n];//n代表背包总体积
for(int i=1;i<=m;i++)
for(int j=v[i];j<=V;j++)
f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);
对上面核心式进行简化,发现
0-1背包:
for(int j=V;j>=v[i];j--)
f[j]=max(f[j],f[j-v[i]]+w[i]);
完全背包
for(int j=v[i];j<=V;j++)
f[j]=max(f[j],f[j-v[i]]+w[i]);
只有循环的顺序不一样
当然,通过以上两个基本式可以衍生出许多变式。我们需要从实际问题中抽象出“决策”,从而达到使用背包式的目的。
以0-1背包为例,实际问题中,对于一个目标物体进行处理时,如主附件问题那样,我们不能使用完全背包,需要用到0-1背包,但是0-1背包的两个决策又不能够满足要求,这个时候就需要添加新的决策来完善条件。如下面这个代码所示:
for(int i=1;i<=m;i++){
for(int j=n;main_item_w[i]!=0&&j>=main_item_w[i];j--){
f[j]=max(f[j],f[j-main_item_w[i]]+main_item_c[i]);
if (j >= main_item_w[i] + annex_item_w[i][1])
f[j] = max(f[j],f[ j - main_item_w[i] - annex_item_w[i][1] ] + main_item_c[i] + annex_item_c[i][1]);
if (j >= main_item_w[i] + annex_item_w[i][2])
f[j] = max(f[j],f[ j - main_item_w[i] - annex_item_w[i][2] ] + main_item_c[i] + annex_item_c[i][2]);
if (j >= main_item_w[i] + annex_item_w[i][1] + annex_item_w[i][2])
f[j] = max(f[j],f[ j - main_item_w[i] - annex_item_w[i][1] - annex_item_w[i][2] ] + main_item_c[i] + annex_item_c[i][1] + annex_item_c[i][2]);
}
}
而上述代码的背包问题其实都是用来解决最大‘价值’问题的,如果我们需要求解方案数问题,
围绕‘决策’这个思路来,对第i个物品做决策有两个,要么选这个物品,要么不选这个物品,这两个方案是需要相加的
只需对代码进行如下更改:
for(int j=n;j>=w[i];j--){
f[j]=f[j]+f[j-w[i]];
}
最后,其实搜索类问题和dp问题有着许多相通之处,搜索问题皆可dp,但dp不一定可以搜索。因此做搜索类问题时可以尝试用记忆化搜索、dp来优化复杂度。