动态规划初探 -- 背包问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划初探 -- 背包问题相关的知识,希望对你有一定的参考价值。

在为期一个星期的ACM集训之后,我就这样做了一个逃兵hhhh

在这一个星期里面,学长讲了快速排序,二分三分搜索,矩阵快速幂,线段树,BFS(广度优先搜索)和DFS(深度优先搜索),邻接表和哈希表,结构体和优先队列,背包问题和动态规划。

其中讲快速排序那天我还在考试,就没有去听,第二天找学长的时候也听得似懂非懂。

学长讲矩阵快速幂的时候爸妈来找我,也没有听。其中基本都学的不扎实。

所以都要后期重新再学一遍,巩固扎实。估计学长还会讲并查集和最小生成树,我就自己看看书吧~

 

=========================================

以上就是背景,下面今天我重新看了看动态规划。动态规划的入门基本逃不出 01背包问题。

可能这是一个基本的必经问题:

有一个包和n个物品,包的容量为m,每个物品都有各自的体积和价值,问当从这n个物品中选择多个物品放在包里而物品体积总数不超过包的容量m时,能够得到的最大价值是多少?

这个问题,学长当时给了几个例子:有三个背包,分别重3,3,5.他们的价值分别是6,6,9.背包的最大的载重量为10.问该怎么选择背包。

我因为比较贪婪,肯定选择价值大的,先选择9。其实这就体现了贪心算法,先只顾眼前的利益。

但是学长说,有些人会先看价值利益的量,比如前两个的背包的价值量是2,第三个背包的价值量是9/5。明显人们应该喜欢小而精美的物品,应该选择前两个的才能更能拿到有价值的东西。

我顿时想想觉得非常有道理。但是如果你仔细想想,如果我选择了前两个,他们的总价值为12,但是他们的总质量是6,不能把第三个物品再装到包里,这就造成了包的浪费。

而如果我选择重量分别为3,5的物品,我的价值总共能够拿到6+9= 15 的物品!

所以我们在选物品的时候要多次比较,一直选一种方案比较到底,然后再进行下一条方案,这里就要用到DFS。

 比如选了物品A,然后选择物品B,或者选择物品C。然后选择物品B的接着选择物品C,而选择物品C的选择物品B。

有点累死二叉树的感觉,但是子节点要比二叉树多。二叉树的基础题可以做一下我们学校的OJ里面的一个基础题:数字三角形 

这道题目我刚开始做得时候其实用了动态规划,但是当时我还是不懂动态规划的思想。

这道题目当时的代码:

#include <stdio.h>

int main(){
    int n=0;
    while(scanf("%d",&n)!=EOF){
        if(n==0){
            break;
        }
        int num[255][255]={};
        int output[255][255]={};
        for(int i=1;i<=n;i++){
            for(int j=1;j<=i;j++){
                scanf("%d",&num[i][j]);
             }
         }
         for(int i=1;i<=n;i++){
            output[n][i]=num[n][i];
          }
          for(int i=n-1;i>=1;i--){
            for(int j=1;j<=i;j++){
                if(output[i+1][j+1]>output[i+1][j]){
                    output[i][j]=output[i+1][j+1]+num[i][j];
                }
                else{
                    output[i][j]=output[i+1][j]+num[i][j];
                }
            }
          }
          printf("%d\n",output[1][1]);
    }
 }

  这个是练手,其实就是从最上面开始看起,先比较下面两个,选择比较大的那一个。这就用到了贪心算法的思想。

咳咳,好像有点跑题了,继续回到背包问题。

用我们学长的话来概括好了:

当我们在考虑第i个物品的时候,实际上我们只需要关心前i-1个物品能组合出什么样的体积,我们并不关心每一种组合到底是什么样的。 因为组合数量实在太多了,我们需要抓住这些组合的“共性”。从来抽象出可以承受的状态数

技术分享

 

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>

using namespace std;

const int N = 100;
int dp[N][N] = {};
int n,m;

struct node{
    int w;
    int v;
    node(int a = 0,int b = 0){
        w = a;
        v = b;
    }
}bags[N];

int main(){
    while(~scanf("%d%d",&n,&m)){
        for(int i = 1;i <= n;i++){
            scanf("%d%d",&bags[i].w,&bags[i].v);
        }
        memset(dp,0,sizeof(dp));
        for(int i = 1;i <= n;i++){
            for(int j = m;j >= 0;j--){
               dp[i][j] = dp[i-1][j];
                if(j >=bags[i].w) dp[i][j] = max(dp[i-1][j],dp[i-1][j-bags[i].w]+bags[i].v);
            }
        }

        for(int i = 1;i <= n;i++){
            for(int j = 0;j <= m;j++){
                printf("%+d ",dp[i][j]);
            }puts("");
        }
        printf("%d",dp[n][m]);
    }    
}

/*
NOTE:
With three bags weight 3,3,5.And the values are 6,6,9
 */

  昨天在杭电的BestCoder来了一发,只能怪自己基础薄弱,只做出第一题,第二题就因为自己考虑非常多就卡住了,然后在那边写了一个多小时还没有写出来。

但正是因为想得非常多,到hack时间的时候就hack掉了一个人,第一次hack成功哈哈

第一次参加BestCoder名字就这么顺利地变绿了^_^而且接近变蓝。我要继续努力,早日变蓝

以上是关于动态规划初探 -- 背包问题的主要内容,如果未能解决你的问题,请参考以下文章

动态规划-第二节:动态规划之背包类型问题

分别用回溯法和动态规划求0/1背包问题(C语言代码)

背包动态规划输入

动态规划问题3--多重背包

动态规划问题3--多重背包

动态规划背包问题总结