动态规划 - 0-1背包问题的算法优化

简单描述

0-1背包问题描述如下:

有一个容量为V的背包,和一些物品。这些物品分别有两个属性,体积w和价值v,每种物品只有一个。要求用这个背包装下价值尽可能多的物品,求该最大价值,背包可以不被装满。因为最优解中,每个物品都有两种可能的情况,即在背包中或者不存在(背 包中有0个该物品或者 1个),所以我们把这个问题称为0-1背包问题。

0-1背包问题状态转移方程

用dp[i][j]表示前i个物品在总体积不超过j的情况下,放到背包里的最大价值。由此可以推出状态转移方程:

dp[0][j] = 0;

dp[i][j] = max{dp[i-1][j-v[i]] + w[i],dp[i-1][j]};

上面的式子应该很好理解,当第i物品的体积小于当前剩余的体积,则说明可以装入背包,那么dp[i][j] = dp[i-1][j-v[i]]+w[i]。反之就是不能转入背包,dp[i][j] = dp[i-1][j]。

0-1背包问题实现算法1

#include <iostream>
using namespace std;

#define MAXSIZE 100
int w[MAXSIZE];
int v[MAXSIZE];
int maxv;
int n;
int dp[MAXSIZE][MAXSIZE];

int max(int a, int b)
{
    if (a > b)
        return a;
    else
        return b;
}

int main()
{
    cin >> n >> maxv;
    for (int i = 1; i <= n; i++)
    {
        cin >> w[i] >> v[i];
    }
    for (int i = 0; i <= maxv; i++)
        dp[0][i] = 0;

    for (int i = 1; i <= n; i++)
    {
        //只有当j >= w[i],dp[i][j]才能进行选取最大值
        for (int j = maxv; j >= w[i]; j--)
        {
            dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
        }

        //当j < w[i],说明第i个物品是不能转入背包的,故dp[i][j] = dp[i-1][j]
        for (int j = w[i] - 1; j >= 0; j--)
            dp[i][j] = dp[i - 1][j];
    }

    cout << dp[n][maxv] << endl;
    return 0;
}

看到这里,还没完,哈哈!下面介绍一下对dp的优化,我们发现这里的dp是一个二维数组,其实dp完全可以用一维数组表示。为啥子???

0-1背包问题实现算法2

看这里:可以发现0-1背包的状态转移方程 dp[i][j] = max{dp[i-1][j-w[i]]+v[i],dp[i-1][j]}的特点,当前状态仅依赖前一状态的剩余体积与当前物品体积v[i]的关系。根据这个特点,我们可以将dp降到一维即dp[j] = max{dp[j],dp[j-w[i]]+v[i]}。从这个方程中我们可以发现,有两个dp[j],但是要区分开。等号左边的dp[j]是当前i的状态,右边中括号内的dp[j]是第i-1状态下的值。

所以为了保证状态的正确转移,我们需要先更新等号左边中的dp[j](当前状态的dp[j])。

#include <iostream>
using namespace std;

#define MAXSIZE 100
int w[MAXSIZE];
int v[MAXSIZE];
int maxv;
int n;
int dp[MAXSIZE];

int max(int a, int b)
{
    if (a > b)
        return a;
    else
        return b;
}

int main()
{
    cin >> n >> maxv;
    for (int i = 1; i <= n; i++)
    {
        cin >> w[i] >> v[i];
    }
    for (int i = 0; i <= maxv; i++)
        dp[i] = 0;

    for (int i = 1; i <= n; i++)
    {
        //只有当j >= w[i],dp[j]才能进行选取最大值,否则dp[j]将不作更新,等于dp[i-1][j]。
        for (int j = maxv; j >= w[i]; j--)
        {
            dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
        }

    }

    cout << dp[maxv] << endl;
    return 0;
}

比较上面的两个算法可以发现,它们的时间复杂度都是O(n*maxv),只有空间复杂度发生变化。后者的空间复杂度得到了优化。

拓展0-1背包问题

哈哈,还没完,继续0-1背包问题,如果在上面的问题加上一个限制条件,所选择的物品必须恰好装满背包,否则输出-1。

同样的给出两种算法,它们的时间复杂度都是一样的,只不过是空间复杂度不同。

空间复杂度为O(n*maxv)的算法

#include <iostream>
using namespace std;

#define MAXSIZE 100
int w[MAXSIZE];
int v[MAXSIZE];
int maxv;
int n;
int dp[MAXSIZE][MAXSIZE];

int max(int a, int b)
{
    if (a > b)
        return a;
    else
        return b;
}

int main()
{
    cin >> n >> maxv;
    for (int i = 1; i <= n; i++)
    {
        cin >> w[i] >> v[i];
    }
    //初始化,当容积为0时,即不能装入,最大价值即为0
    for (int i = 1; i <= n; i++)
    {
        dp[i][0] = 0;
    }

    //初始化为-1,表示没有装满
    for (int i = 0; i <= n; i++)
    for (int j = 1; j <= maxv; j++)
        dp[i][j] = -1;

    for (int i = 1; i <= n; i++)
    {
        for (int j = maxv; j >= w[i]; j--)
        {
            //dp[i - 1][j - w[i]] != -1表示容积为j - w[i]时没有装满,所以当容积为j,装w[i]时一定不能装满
            //dp[i - 1][j - w[i]] + v[i] > dp[i-1][j]表示装入物品i时签好装满并且总价值比前i-1个物品的总价值要大
            if (dp[i - 1][j - w[i]] != -1 && dp[i - 1][j - w[i]] + v[i] >= dp[i - 1][j])
                dp[i][j] = dp[i - 1][j - w[i]] + v[i];

        }
        for (int j = w[i] - 1; j >= 1; j--)
            dp[i][j] = dp[i - 1][j];
    }

    cout << dp[n][maxv] << endl;

    return 0;
}

空间复杂度为O(maxv)的算法

#include "stdafx.h"
#include <iostream>
using namespace std;

#define MAXSIZE 100
int w[MAXSIZE];
int v[MAXSIZE];
int maxv;
int n;
int dp[MAXSIZE];

int max(int a, int b)
{
    if (a > b)
        return a;
    else
        return b;
}

int main()
{
    cin >> n >> maxv;
    for (int i = 1; i <= n; i++)
    {
        cin >> w[i] >> v[i];
    }
    //初始化,当容积为0时,即不能装入,最大价值即为0
    dp[0] = 0;

    //初始化为-1,表示没有装满
    for (int j = 1; j <= maxv; j++)
        dp[j] = -1;

    for (int i = 1; i <= n; i++)
    for (int j = maxv; j >= w[i]; j--)
    {
        if (dp[j - w[i]] != -1 && dp[j - w[i]] + v[i] >= dp[j])
            dp[j] = dp[j - w[i]] + v[i];
    }
    cout << dp[maxv] << endl;

    return 0;
}

从上面的算法我们发现,这里的状态转移方程和0-1背包问题的状态转移方程是一样一样滴,只不过是初试状态发生了一点改变。

呵呵,到这里0-1背包问题先结束了,后面会继续介绍更加复杂的背包问题。

ps:走一步,学一步,总结一步,路就不会太远,目标也不会遥不可及。

时间: 2024-12-28 12:05:56

动态规划 - 0-1背包问题的算法优化的相关文章

动态规划0—1背包问题

动态规划0-1背包问题 ? 问题描写叙述: 给定n种物品和一背包.物品i的重量是wi,其价值为vi,背包的容量为C.问应怎样选择装入背包的物品,使得装 入背包中物品的总价值最大? ? 对于一种物品,要么装入背包,要么不装.所以对于一种物品的装入状态能够取0和1.我们设物品i的装入状态为xi,xi∈ (0,1),此问题称为0-11背包问题. 过程分析 数据:物品个数n=5,物品重量w[n]={0,2,2,6,5,4},物品价值V[n]={0,6,3,5,4,6}, (第0位,置为0,不參与计算,仅

动态规划--0,1背包问题(再也不怕类似背包问题了)

这种类型问题三大要素:总重量.每件物品重量.每件物品价值,问最终能够塞进背包中的价值最大是多少?应该怎么选择物品? 当然也不一定是这些,例如上节所说的矿工挖矿:总人数.挖每座矿的人数.每座矿的金子数. 也就是说,只要出现了这三大要素,都可以视为0,1背包问题(物品不可拆分) 动态规划三要素:边界.最优子结构.状态转移方程. 我们一步步进行解析: 初始化:物品总重量:c=8,物品类别:n=['a','b','c','d'],物品重量:w=[2,4,5,3],物品价值:v=[5,4,6,2] 假设我

动态规划算法求解0,1背包问题

首先我们来看看动态规划的四个步骤: 1. 找出最优解的性质,并且刻画其结构特性: 2. 递归的定义最优解: 3. 以自底向上的方式刻画最优值: 4. 根据计算最优值时候得到的信息,构造最优解 其中改进的动态规划算法:备忘录法,是以自顶向下的方式刻画最优值,对于动态规划方法和备忘录方法,两者的使用情况如下: 一般来讲,当一个问题的所有子问题都至少要解一次时,使用动态规划算法比使用备忘录方法好.此时,动态规划算法没有任何多余的计算.同时,对于许多问题,常常可以利用其规则的表格存取方式,减少动态规划算

动态规划算法实现部分——0/1背包问题

代码: import java.util.*; import java.util.Scanner; /* *动态规划思想解决0/1背包问题 */ public class Main{ public static void main(String[] args){ Scanner in=new Scanner(System.in); System.out.println("输入背包的容量"); int bagCap=in.nextInt(); //背包的容量 System.out.pri

第十六章 贪心算法——0/1背包问题

1.问题描述: 给定n种物品和一背包.物品i的重量是wi,其价值为vi,背包的容量为C.问:应如何选择装入背包的物品,使得装入背包中物品的总价值最大? 形式化描述:给定c >0, wi >0, vi >0 , 1≤i≤n.要求找一n元向量(x1,x2,…,xn,), xi∈{0,1}, ∋ ∑ wi xi≤c,且∑ vi xi达最大.即一个特殊的整数规划问题. 2.最优性原理: 设(y1,y2,…,yn)是 (3.4.1)的一个最优解.则(y2,…,yn)是下面相应子问题的一个最优解:

背包问题:0/1背包问题 普通背包问题(贪心算法只适用于普通背包问题)

//sj和vj分别为第j项物品的体积和价值,W是总体积限制. //V[i,j]表示从前i项{u1,u2,…,un}中取出来的装入体积为j的背包的物品的最大价值. 第一种:0/1背包问题 最大化 ,受限于  1)若i=0或j=0,  V[i,j] = 0 2)若j<si, V[i,j] = V[i-1,j] 3)若i>0且j>=si, V[i,j] = Max{V[i-1,j],V[i-1,j-si]+vi} 第二种:背包问题:在选择物品i装入背包时,可以选择物品i的一部分,而不一定要全部

[转载]动态规划之0-1背包问题

作者:Hawstein出处:http://hawstein.com/posts/dp-knapsack.html 一切都要从一则故事说起. 话说有一哥们去森林里玩发现了一堆宝石,他数了数,一共有n个. 但他身上能装宝石的就只有一个背包,背包的容量为C.这哥们把n个宝石排成一排并编上号: 0,1,2,-,n-1.第i个宝石对应的体积和价值分别为V[i]和W[i] .排好后这哥们开始思考: 背包总共也就只能装下体积为C的东西,那我要装下哪些宝石才能让我获得最大的利益呢? OK,如果是你,你会怎么做?

动态规划解决0-1背包问题

这几天算法老师布置了一个作业是关于背包问题的,在这里记录一下解决的过程: 一:背包问题的描述 有n1,n2,n3....个物品,每个物品的重量为w1,w2,w3.....,每个物品的价值为v1,v2,v3....,现在有一个承重量为C的背包,求出要怎样放物品才能使的装入背包的物品价值总和最大.由于每个物品只有一个,并且只能选择放或不放,因此用0表示物体没有放进背包中,1表示物体放进背包中 二:分析过程(以具体的例题来分析) 题目描述: 现有一个可以承重为6的背包bag,以及3个物品,它们的重量分

[算法] 优化算法 梯度下降

导数 导数是一个数,函数y(x)在x0点的导数y'(x0)反应了在x0处y随x的变化快慢 微分 微分指函数值的微小变化 在x0可微:在x0点y和x的微分成线性关系(只与该点函数值有关) 导数可看做函数的微分与自变量的微分之商,故导数又称微商 偏导数 函数在一点处沿坐标轴的变化率 方向导数 函数在一点处沿射线方向的变化率 偏导数是双侧的,方向导数是单侧的.函数f(x,y)在一点处对x偏导数等于沿x轴正向的方向导数 梯度 梯度是一个向量 方向导数沿梯度方向取最大值,最大值为梯度的模,即沿梯度方向函数