[动态规划]背包问题九讲(1)----01背包问题

由于最近是清明节比较闲,而且一直对动态规划的问题比较头疼,动态规划是一个很重要的概念,因此这个清明节我决定重新看动态规划,希望有新的认识。那么我就从网上流传比较广的背包九讲入手,希望能在清明节的假期之内更新九篇,用我自己的话语来解释,希望大家看了我写的之后能够有所收获。

接下来进入正题:   01背包问题

01背包问题是背包问题里面可以说是最简单的一个问题,先来介绍一下题目:背包承重为W,有N个物品,每个物品分别为w[1]...w[n]重量,并且每个物品都有自己的价值v[i]...v[n],求背包能放入的最大价值。

这里我就不解释为什么贪心法无法解这类问题,直接开始分析。

首先我们可以假设每个物品最后都会都有一个状态,是否放入背包。

那么动态规划最重要的是转移方程,写出转移方程也就代表这道题目已经解出来了。我们假定f[i][w]表示对于前i个物品,背包承重为w的时候能够得到的最大价值。

我们可以有 f[i][w] = max( f[i-1][w] , f[i-1][w-w[i]]+v[i]  )

那么这个方程如何得到呢,前面我提到了一个前提:每个物品最后都会都有一个状态 即 是否放入背包。那么f[i-1][w]就代表i物品不放入背包,那么最大价值就是将前i-1个物品放入背包的最大价值,相对的f[i][w-w[i]]+v[i]就代表如果i放入背包的最大价值。这样一来我们就得到了伪代码:

for i 1...N
	for w 1...W
		f[i][w] = max( f[i-1][w], f[i][w-w[i]]+v[i]  )

那么我们这个算法的时间复杂度为O(W*N),空间复杂度也为O(W*N)

对于一个程序,我们不仅需要优化时间,还需要优化空间,那么很显然的是,对于时间复杂度我们不太可能再优化,于是我们来看看空间复杂度能否优化。答案是肯定的

转移方程    f[w] = max( f[w],f[w-w[i]]+v[i]  )

复杂度为O(W)。

接下来给出源程序,空间复杂度分别为O(N*W) ,O(W):

 O(N*W):

#include <iostream>
using namespace std;
int w[100],v[100];
int f[101][101];
int main(){
    int W,N;
    cin >> W >> N;
    for( int i = 1; i <= N; ++i ){
        cin >> w[i] >> v[i];
    }
    for( int i = 1; i <= N; ++i ){
        for( int j = 0; j <= W; ++j ){
            if( j >= w[i] )
                f[i][j] = f[i-1][j] >= f[i-1][j-w[i]]+v[i] ? f[i-1][j] : f[i-1][j-w[i]]+v[i];
            else
                f[i][j] = f[i-1][j];
        }
    }
    cout << f[N][W];
    return 0;
}

O(W):

#include <iostream>
using namespace std;
int w[100],v[100];
int f[101];
int main(){
    int W,N;
    cin >> W >> N;
    for( int i = 1; i <= N; ++i ){
        cin >> w[i] >> v[i];
    }
    for( int i = 1; i <= N; ++i ){
        for( int j = W; j >= 0; --j ){
            if( j >= w[i] )
                f[j] = f[j] >= f[j-w[i]]+v[i] ? f[j]:f[j-w[i]]+v[i];
        }
    }
    cout << f[W];
    return 0;
}

这里如果我们需要打印出我们选择的是哪些物品,那么应该如何修改程序呢?有两种方法,第一种是加数组,第二种是倒推。我就来讲一下思路吧。

加数组:

    当加入i这个物品就能够使得容量为w的背包价值最大,则将该物品加入另外的数组w中(当然,需要将之前i-1物品的状态记录下来)那么,最后W中就是所有的物品。

逆推:

    如果第i个物品不放入,则f[i][W]==f[i-1][W],那么根据这个条件,我们就可以反推,得到物品i是否加入了背包。

时间与空间,常常会像这样无法兼得。

代码我就不实现了,比较简单,只需在程序上稍加修改即可。

最后写完这篇发现用了一天多,看来清明的假期无法让我完成9篇了,那希望这学期能够完成吧。国际惯例,壁纸一张,这回是汽车。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值