C语言学习笔记(XI)---动态规划之“01背包”

“01背包”问题大概是最基础的dp问题了,当学到dp算法的时候,老师讲到的第一个问题往往就是“01背包”。我也是前前后后看过很多次,也学过几次,但总是学一次忘一次。今天为此写一个博客,写一个简单的笔记,希望借此机会加强理解。

01背包

问题描述

这个问题在出现时一般会加以一定特殊情境的描述,比如在森林里探险,要出去野餐……但问题的核心都是一样的:
有N件物品和一个容量为V的背包。第i件物品的体积是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。

求解思路

有的背包问题可以使用贪心算法来求解,但对于01背包问题来说,使用贪心算法往往不能得到最优解,所以考虑动态规划。
动态规划是通过把一个问题分解为较小的子问题,先求解子问题并将结果储存,最后合并这些子问题的解得到原问题的最优解。
在动态规划问题中,有两个关键的概念:状态和状态转移方程。
“状态”用来描述该问题的子问题的解。
“状态转移方程”用来描述状态之间的转移过程。
所以首先思考01背包的状态是什么。

考虑你有N=3件物品和一个容量为V=5的背包。三件物品的体积分别是c[1]=1,c[2]=5,c[3]=3;价值分别是w[1]=20,w[2]=1,w[3]=50。简单一想,肯定是选择1号和2号物品总价值最高啊。此时占用空间为4,总价值为70。这是最后的状态。
往前推一个状态,也就是把3号物品除去,又因为它是被选中的物品,所以在背包里把他的体积也减掉,就成了n=2,v=2的问题。结果是选1号物品价值最高。这又是一个状态。再往前推,也是一个状态。
我们用f[i][j]表示在前i个物品中选择总体积不超过j的物品所能获得的最大价值,那么我们最后要求解的就是f[N][V]。
那状态之间如何转移呢。

在我看来,状态之间的差别无非在于某一个物品取还是不取。假设我们已经有了i-1状态的解。那么转移后的状态f[i][j]有两种可能的值,一种是取i号物品,我们得到f[i][j]=f[i-1][j-c[i]]+w[i];一种是不取,我们得到f[i][j]=f[i-1][j]。二者取大即可。
于是得到状态转移方程:f[i][j]=max{f[i-1][j-c[i]]+w[i], f[i-1][j]}

C语言示例代码

//
//  main.c
//  bag01
//
//  Created by passer_by_a on 2018/1/10.
//  Copyright © 2018年 passer_by_a. All rights reserved.
//

#include <stdio.h>

int max(int a,int b){//最大值函数
    return (a>b)?a:b;
}

int main() {
    int N,V;
    int c[100]={0};//储存物品费用
    int w[100]={0};//储存物品价值
    int f[100][100]={0};//f[i][j]应该是从前i件物品中占用j点空间所能获得的最大价值
    scanf("%d %d",&N,&V);
    for(int i=1;i<=N;i++){
        scanf("%d %d",&c[i],&w[i]);
    }

    for(int i=1;i<=N;i++){
        for(int j=0;j<=V;j++){
            f[i][j]=f[i-1][j];
            if(j>=c[i])
                f[i][j]=max(f[i-1][j-c[i]]+w[i],f[i-1][j]);//状态转移方程且防止边界溢出
        }

    printf("%d\n",f[N][V]);
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值