0-1背包问题

【01背包】
有N件物品和一个载重为W的背包。第i件物品的重量是 w[i] ,价值是 v[i]
求解背包最多能装下的价值是多少?
【解法一 动态规划】
思路分析:
对于每件物品,只有两种情况:装?不装?。
假设dp[i][j]表示在背包剩余载重j的情况下,前i个物品能达到的最大价值。
对于第i个物品,
如果w[i]>j,放不进去,不能放,故dp[i][j]=dp[i-1][j]

如果w[i]<=j,根据每种物品是否放入背包,有两种可能,
【1】不放 dp[i][j]=dp[i-1][j]
【2】放 dp[i][j]=dp[i-1][j-w[i]]+v[i]
取两者较大者即可
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])
状态转移方程
dp[i][j]=dp[i-1][j] (w[i]>j)放不进
dp[i][j]=max (dp[i-1][j], dp[i-1][ j-w[i] ] +v[i])放和不放取较大的。

【实例】
这里写图片描述

下图是对本题的实际模拟。
这里写图片描述
初始状态
no items时,无论包载重多少,都装不了任何价值的东西,故dp[0][i]=0;
载重或capacity=0时,不能转任何重量的物品,故dp[i][0]=0;
后的dp[x][y]按状态转移方程,推出即可。可以手动填入数据理解算法原理。
附上在线模拟背包问题连接
0-1背包问题在线模拟网址

【附上代码】

#include<iostream>
#include<stdio.h>
using namespace std;
int n=5;
int w[6]={0,4, 6, 5, 7,3};
int v[6]={0,12,10,8,11,14};
int dp[6][17]={0}; 
int main()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=16;j++)
        {
            if(j<w[i])
            {
                dp[i][j]=dp[i-1][j];
            }
            else
            {   //放进去 
                int temp=dp[i-1][j-w[i]]+v[i]; 
                dp[i][j]=dp[i-1][j];
                if(temp>dp[i][j])
                dp[i][j]=temp;  
            }

        }
    }
    for(int i=0;i<6;i++)
    {
        cout<<"item:"<<i<<" ";
        for(int j=0;j<17;j++)
        {
            printf("%4d",dp[i][j]);
        } 
        cout<<endl;

    }
}

运行截图
这里写图片描述

【解法二 递归】
思路分析:
对于每种物品无非是两种状态 放 or 不放。用一个bool型的一维数组choose记录是否放入,用0 1 表示这两种状态,choose[i]=true,表示放入,choose[i]=false,表示第i件物品不放入。
对于第i个物品,当我们尝试了其中一种状态(true或false),便可以尝试是否放入下一个i+1 ,然后对i尝试另外一种状态。
当把所有的物品尝试完了后,便得到了一种组合情况,统计这种放入的组合情况是否超过背包载重以及是否大于已经记录的最大价值。

#include<iostream>
using namespace std;
int n=5;//物品数目 
bool choose[6]={0};//记录是否装入; 
bool res[6]={0};//记录当前最大价值时的装入情况。 
int w[6]={0,4, 6, 5, 7,3};//重量 
int v[6]={0,12,10,8,11,14};//价值 
int maxw=16;//背包载重值 
int maxv=0;//初始最大价值。

int sumw()//统计装入的重量 
{
    int total=0;
    for(int i=1;i<=5;i++)
    {
        if(choose[i]==1)
        {
            total+=w[i];
        }
    }
    return total;
}
int sumv()//统计装入的价值 
{
    int total=0;
    for(int i=1;i<=5;i++)
    {
        if(choose[i]==1)
        {
            total+=v[i];
        }
    }
    return total;
}
void dfs(int x)//递归,尝试将所有可能情况遍历, 
{
    if(x>n)
    {
        int tempw=sumw();
        int tempv=sumv();
        if(tempw<=maxw&&tempv>maxv)//装入重量在背包载重内,
        //并且此时装入放背包的价值大于之前记录的最大价值 
        {
            maxv=tempv;
            for(int i=1;i<=5;i++)
            {
                res[i]=choose[i];
            }
        }
    }
    else
    {
        choose[x]=true;
        dfs(x+1);
        choose[x]=false;
        dfs(x+1);
    }
}
int main()
{
    dfs(1);
    cout<<"max value: "<<maxv<<endl;
    for(int i=0;i<=5;i++)
    {
        if(res[i]==1)
        {
            cout<<"items: "<<i<<" W:"<<w[i]<<" V:"<<v[i]<<endl;
        }
    }
 } 

运行截图
这里写图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Casionx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值