算法—动态规划(2)0-1背包问题回顾

算法—动态规划(2)0-1背包问题回顾

0-1背包问题应该算是我本人第一个接触的dp问题,不过当时确实是没弄懂。今天重新回顾了一下,不禁又有了新的收获

问题描述

n个物品,重量和价值分别为 w i w_i v i v_i ,背包总的承重力为W,求能装入的最大价值。

问题分析

相比起一维dp,这里的问题变成了二维的:我要求的是价值,但我又不得不考虑重量的限制。
我们先来考虑一下边界状态:
1) w i > W w_i>W ,那么很明显,直接GG,装不了;
2) w i < W \sum w_i <W ,那么都可以装;
好,这两种边界状态下,那么第一个是0;第二个是求和。
那么正常的转态下呢:
我们设函数 d p ( i , j ) dp(i,j) 表示:当总重量小于j时(也就是说背包还可以装),从下标为i的商品开始挑选,得到的商品的最大值。
我们都知道,dp问题就是一个在动态中“选or不选”的问题。前面已经提到了,如果单件商品重量大于承重范围,那就直接不选了,跳过下一个;如果符合要求,那就看当前这一物品的选入是否比下一物品的选入更有价值。
即状态转移方程如下:
d p ( i , j ) = d p ( i + 1 ) ( j ) j < w [ j ] dp(i,j)=dp(i+1)(j) j<w[j]
d p ( i , j ) = m a x [ d p ( i + 1 , j ) , d p ( i + 1 , j w [ j ] v [ i ] ) ] dp(i,j)=max[dp(i+1,j),dp(i+1,j-w[j]-v[i])]
解释一下该方程:
第一行意为:此时重量是符合的,那我就放进去;
第二行:此时重量符合,承重余量面临不足,那么我就考虑了:是放进去还是不放进去,跳过看下一个?
由于j表示放,那么j-w[i]-v[i]就表示不放。
在此我们还有个问题?我们是否需要全部搜索一遍?其实不必,已经搜索过的,就可以跳过。
那么下面来看一下代码:

代码

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxN=3405;
const int maxW=405;
int dp[maxN][maxW];
int N,W;
int w[maxW],v[maxN];
int rec(int i,int j){    
    if(dp[i][j]>=0) return dp[i][j];//记忆化搜索,搜过的就变成正数了。初始化为-1是防止特殊情况出现
    int ans;
    if(i==N) ans=0;//超重,一个都放不了
    else if(j<w[i]) ans=rec(i+1,j);//能放,一直放
    else ans=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);//要开始挑了
    return dp[i][j]=ans;
}
int main(){
    memset(dp,-1,sizeof(dp));
    cin>>N>>W;
    for(int i=0;i<N;i++)
    {
        cin>>w[i];
        cin>>v[i];
    }
    int res=rec(0,W);
    cout<<res<<endl;
    return 0;
}
}

总结

0-1背包问题相比入门级别的dp问题,扩展到了二维,即“目标”是一个维度,而限制条件又是另一个维度的事。因此在考虑问题的同时,要更加注重临界转态。另外,在考虑搜索问题的同时,又要注意减枝问题,可采用对数组统一初始化的思路。

发布了15 篇原创文章 · 获赞 16 · 访问量 1086

猜你喜欢

转载自blog.csdn.net/weixin_44522586/article/details/103265082