01背包问题(动态规划DP)

来源:互联网 发布:uvz for mac 编辑:程序博客网 时间:2024/05/22 12:00

原题

01背包问题

有n个重量和价值分别为wi,vi的物品。从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值。
1<=n<=100 
1<=wi,vi<=100
1<=W<=10000

样例输入


n=4
(w,v)={(2,3),(1,2),(3,4),(2,2)}
W=5

样例输出


7(选择0、1、3号物品)

代码

递归写法(记忆化搜索)
int n,W;int w[MAX_N],v[MAX_N]//记忆化数组int dp[MAX_N+1][MAX_N+1];//从第i个物品开始挑选总重小于j的部分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<w[i])    {        //无法挑选这个物品        res=rec(i+1,j);    }    else    {        //不挑选和挑选两种情况都尝试一下        res=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);    }    return dp[i][j]=res;}void solve(){    memset(dp,-1,sizeof(dp));    printf("%d\n",rec(0,W));}

递推写法(逆向进行)
dp[i][j]定义为从第i个物品开始挑选总重小于j的部分
dp[n][j]=0
dp[i][j]= dp[i+1][j] (j<w[i])
dp[i][j]= max(dp[i+1][j],dp[i+1][j-w[i]]+v[i]) (j>=w[i])

int n,W;int w[MAX_N],v[MAX_N]//DP数组int dp[MAX_N+1][MAX_N+1];void solve(){    for(int j=0;j<=W;j++)    {        dp[n][j]=0;    }    for(int i=n-1;i>=0;i--)    {        for(int j=0;j<=W;j++)        {            if(j<w[i])            {                dp[i][j]=dp[i+1][j];            }            else             {                dp[i][j]=max(dp[i+1][j],dp[i+1][j-w[i]]+v[i]);            }        }    }    printf("%d\n",dp[0][W]);}
递推写法(正向进行)
dp[i+1][j]定义为从前i个物品中选出总重量不超过j的物品时总价值的最大值
dp[0][j]=0
dp[i+1][j]=dp[i][j] (j<w[i])
dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]) (j>=w[i])
int n,W;int w[MAX_N],v[MAX_N]//DP数组int dp[MAX_N+1][MAX_N+1];void solve(){    for(int j=0;j<=W;j++)    {        dp[0][j]=0;    }    for(int i=0;i<n;i++)    {        for(int j=0;j<=W;j++)        {            if(j<w[i])            {                dp[i+1][j]=dp[i][j];            }            else             {                dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);            }        }    }    printf("%d\n",dp[n][W]);}
状态转移
从“前i个物品中选取总重不超过j时的状态”向“前i+1个物品中选取总重不超过j”和“前i+1个物品中选取总重不超过j+w[i]时的状态”的转移。
int n,W;int w[MAX_N],v[MAX_N]//DP数组int dp[MAX_N+1][MAX_N+1];void solve(){    memset(dp,0,sizeof(dp));    for(int i=0;i<n;i++)    {        for(int j=0;j<=W;j++)        {            dp[i+1][j]=max(dp[i+1][j],dp[i][j]);            if(j+w[i]<=W)            {                dp[i+1][j+w[i]]=max(dp[i+1][j+w[i]],dp[i][j]+v[i]);            }        }    }    printf("%d\n",dp[n][W]);}
注:文章内容源于《挑战程序设计竞赛》(第二版)