0-1背包 动态规划

0-1背包-动态规划

问题描述

超市允许顾客使用一个可以承受重量为c的背包,选择一件或多件商品带走
每种商品有对应的价值和重量,如何带走总价最多的商品?

输入数据

第一行输入物品数量n和背包承重c。
接下来是n行数据,表示每个商品的价值和重量。

输出数据

一共2行
第1行输出最优解,即商品的最高总价。
第2行输出选择的商品编号。

输入输出样例

输入:

5 13
24 10
2 3
9 4
10 5
9 4

输出:

最优解为28 选择的商品为: 3 4 5

思路分析

构造备忘录f[i,c] ,表示在前i个商品中选择,背包容量为c时的最优解
对第i个商品进行分析

  • 若选择商品i,价值为f[i-1,c-w[i] ] +v[i],即从前i-1个里面选择c减去当前商品重量w[i]的商品;
  • 若不选商品i,价值为f[i-1,c],相当于从前i-1件商品里面选择重量为c的商品

由此可得递推公式 f[i,c]=max{ f[i-1,c-w[i]] +v[i] , f[i-1,c]}
接下来我们只需要逐步计算二维数组f中的各个数值,最后得到最优解f[n,c].

最优方案追踪

对于决策过程,另外定义一个rec二维数组,rec[i,c]的值为1说明此情况下的最优解包含商品i,为0则说明不包含。
最后回溯子问题可以得到选择的商品,定义flag标识数组,表示商品i是否在最优方案中

样例求解过程

在这里插入图片描述
这里Vi表示重量,Pi表示价值
最优解即为右下角的28.

Code

//
//  main.cpp
//  0-1背包-动态规划法
//
//  Created by MacBook Pro on 2021/3/30.
//

#include <iostream>
using namespace std;
void Knapsack(int v[], int w[], int n,int c,int **f,int **rec ,bool *flag)
{
    int i;
    for(i=1;i<=n;i++)
    {
        for(int j=1;j<=c;j++)
        {
            if(w[i]<=j&&v[i]+f[i-1][j-w[i]]>f[i-1][j])//当前商品重量小于背包容量且选择商品i价值更大
            {
                f[i][j]=v[i]+f[i-1][j-w[i]];
                rec[i][j]=1;//记录选择
            }
            else
            {
                f[i][j]=f[i-1][j];
                rec[i][j]=0;
            }
        }
    }
    //最优方案追踪
    for(i=n;i>=1;i--)
    {
        if(rec[i][c]==1)//选择商品i
        {
            c-=w[i];//回溯子问题
            flag[i]=1;//标记为选择
        }
        else
        {
            flag[i]=0;//标记为不选
        }
    }
}
int main()
{
    int n,i;
    cin>>n;
    int c;//背包容量
    cin>>c;
    int v[n+1],w[n+1];
    bool flag[n+1];//最优解是否包含商品i
    for(i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
    }
    int **f=new int *[n+1];
    int **rec=new int *[n+1];
    for(i=0;i<=n;i++)
    {
        f[i]=new int[c+1];
        rec[i]=new int [c+1];
    }
    for(i=0;i<=c;i++)
    {
        f[0][i]=0;
        rec[0][i]=0;
    }
    for(i=0;i<=n;i++)
    {
        f[i][0]=0;
        rec[i][c]=0;
    }
    Knapsack(v,w,n,c,f,rec,flag);
    cout<<"最优解为"<<f[n][c]<<endl;
    cout<<"选择的商品为: ";
    for(i=1;i<=n;i++)
    {
        if(flag[i])
            cout<<i<<' ';
    }
    cout<<endl;
    for(i=0;i<=n;i++)
        delete []f[i];
    delete []f;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Pretend ^^

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

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

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

打赏作者

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

抵扣说明:

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

余额充值