01背包问题(0,1的意思就是只能取一次或者不取)
有n个重量和价值分别为wi和vi的物品。从这些物品中挑选出总质量不超过w的物品,求所有挑选方案中质量和的最大值。
如果我去模拟一下这个问题:每个物品都可以选择或不选择。假设我从第i个物品挑选总重量小于j的物品:
int rec(int i, int j){
int res;
if(i == n) res = 0;
else if(j < wei[i]) res = rec(i + 1, j); //无法挑选此物品,挑选下一个物品
else res = max(rec(i + 1, j), rec(i + 1, j - wei[i]) + val[i]);
}
rec(i + 1, j)表示不选择第i个物品 rec(i + 1, j - wei[i]) + val[i] 表示选择了第i个物品,所以要加上第i个物品的质量,然后进行比较,取使得质量最大的情况
递归调用的过程中会出现重复的现象,为了避免这一现象的发生,我们可以将返回的结果记录下来,使得下一次不在去计算。
int n, w;
int wei[maxn], val[maxn];
int dp[maxn][maxn]; //用数组去记录,如果算过记为1,没有算过记为0
int rec(int i, int j){
if(dp[i][j] >= 0) return dp[i][j]; //计算过的话直接使用之前的结果
int res;
if(i == n) res = 0;
else if(j < wei[i]) res = rec(i + 1, j);
else res = max(rec(i + 1, j), rec(i + 1, j - wei[i]) + val[i]);
return dp[i][j] = res;
}
void solve(){
memset(dp, -1, sizeof(dp));
printf("%d\n", rec(0, w));
}
int main()
{
while(scanf("%d%d", &n, &w) != EOF){
solve();
}
}
将此代码写为递归方程 :dp[i][j] = 0;
dp[i][j] = dp[i + 1][j] 当j <wei[i];
dp[i][j] = max(dp[i + 1][j], dp[i + 1][j - wei[i]] + val[i]); 当j>=wei[i];
写为for循环:
for(int i = n-1; i >= 0; i--){
for(int j = 0; j <= w; j++){
if(j < wei[i]) dp[i][j] = dp[i+1][j];
else dp[i][j] = max(dp[i+1][j], dp[i+1][j-wei[i]] + val[i]);
}
}
printf("%d\n", dp[0][w]);
来源于:挑战