zoukankan      html  css  js  c++  java
  • 【动态规划】背包问题(一) 01背包 完全背包 多重背包

    一、01背包

    有N件物品和一个容量为V的背包。第i件物品的价格(即体积,下同)是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

    这是最基础的背包问题,总的来说就是:选还是不选,这是个问题<( ̄ˇ ̄)/

    相当于用f[i][j]表示前i个背包装入容量为v的背包中所可以获得的最大价值。

    对于一个物品,只有两种情况

      情况一: 第i件不放进去,这时所得价值为:f[i-1][v]

      情况二: 第i件放进去,这时所得价值为:f[i-1][v-c[i]]+w[i] 

    状态转移方程为:f[i][v] = max(f[i-1][v], f[i-1][v-w[i]]+c[i])

    一道裸01背包题↓_↓

    采药

    题目描述 Description

    辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。” 

    如果你是辰辰,你能完成这个任务吗?

    输入描述 Input Description

    输入第一行有两个整数T(1<=T<=1000)和M(1<=M<=100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

    输出描述 Output Description

    输出包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

    样例输入 Sample Input

    70 3

    71 100

    69 1

    1 2

    样例输出 Sample Output

    3

    数据范围及提示 Data Size & Hint

    【数据规模】

    对于30%的数据,M<=10;

    对于全部的数据,M<=100。

    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    int f[1001][1001];
    int main()
    {
        int T, n,c[10001], v[10001];
         scanf("%d%d", &T, &n);
         for(int i = 1; i <= n; i++)
           scanf("%d%d", &v[i], &c[i]);
         for(int i = 1; i <= n; i++)
         {
             for(int j = 0; j <= T; j++)
                f[i][j] = f[i-1][j];
              for(int j = 0; j+v[i] <= T; j++)
               f[i][j] = max(f[i][j] + c[i], f[i-1][j+v[i]]);
         }
         int ans = 0;
         for(int i = 0; i <= T; i++)
           ans = max(ans, f[n][i]);
         printf("%d", ans);
         return 0;  
    }

    还可以用一维数组这样写↓_↓

    设 f[v]表示重量不超过v公斤的最大价值, 则f[v]=max(f[v],f[v-w[i]]+c[i]) ,当v>=w[i],1<=i<=n

    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    const int maxm = 2001, maxn = 101;
    int m, n;
    int w[maxn], c[maxn];
    int f[maxm]; 
    int main()
    {
        scanf("%d%d",&m, &n);            //背包容量m和物品数量n
        for (int i=1; i <= n; i++)
            scanf("%d%d",&w[i],&c[i]);     //每个物品的重量和价值
        for (int i=1; i <= n; i++)             //设f(v)表示重量不超过v公斤的最大价值
            for (int v = m; v >= w[i]; v--)  //注意是逆序
                f[v] = max(f[v-w[i]]+c[i], f[v]);
        printf("%d
    ",f[m]);                      // f(m)为最优解
        return 0;
    }

    二、完全背包

    有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

    完全背包和01背包十分相像, 区别就是完全背包物品有无限件。由之前的选或者不选转变成了选或者不选,选几件。√

    和01背包一样,我们可以写出状态转移方程:f[i][v]=max(f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v)

    还有一个简单的优化↓_↓

    当一个物品的价值小于另一个物品的价值,但是价格高于另一个物品,我们就可以不去考虑这个物品。即若两件物品i、j满足c[i]<=c[j]且w[i]>=w[j],则将物品j去掉,不用考虑。我们为什么要买一个又贵又难吃的东西呢(╯▽╰)

    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    const int maxm=2001,maxn=101;
    int n,m,v,i;
    int c[maxn],w[maxn];
    int f[maxm];
    int main()
    {
        scanf("%d%d",&m,&n);            //背包容量m和物品数量n
        for(i=1;i<=n;i++) 
            scanf("%d%d",&w[i],&c[i]);
        for(i=1;i<=n;i++)
            for(v=w[i]; v<=m; v++)          //设 f[v]表示重量不超过v公斤的最大价值
                                            //这里是v++ 顺序 区别于01背包 
                f[v]=max(f[v-w[i]]+c[i], f[v]);
        printf("%d
    ", f[m]);           // f[m]为最优解
        return 0;
    }

    三、多重背包

    有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

    这里又多了一个限制条件,每个物品规定了可用的次数。

    同理,我们可以得出状态转移方程:f[i][v]=max(f[i-1][v-k*w[i]]+ k*c[i]|0<=k<=n[i])

    一道例题↓_↓

    庆功会

    【问题描述】

    为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。

    【输入格式】

    第一行二个数n(n<=500),m(m<=6000),其中n代表希望购买的奖品的种数,m表示拨款金额。 接下来n行,每行3个数,v、w、s,分别表示第I种奖品的价格、价值(价格与价值是不同的概念)和购买的数量(买0件到s件均可),其中v<=100,w<=1000,s<=10。

    【输出格式】

    第一行:一个数,表示此次购买能获得的最大的价值(注意!不是价格)。

    【输入样例】

    5 1000

    80 20 4

    40 50 9

    30 50 7

    40 30 6

    20 20 1

    【输出样例】

    1040

    先给出一个未优化的朴素算法

    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    int v[6002], w[6002], s[6002];
    int f[6002];
    int n, m;
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i = 1; i <= n; i++)
            scanf("%d%d%d",&v[i],&w[i],&s[i]);
        for (int i = 1; i <= n; i++)
           for (int j = m; j >= 0; j--)
              for (int k = 0; k <= s[i]; k++)
              {
                   if (j-k*v[i]<0) break;
                   f[j] = max(f[j],f[j-k*v[i]]+k*w[i]);
              }
        printf("%d
    ",f[m]);
        return 0;
    } 

    进行二进制优化,转换为01背包(拆分物品)

    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    int v[10001],w[10001];
    int f[6001];
    int n,m,n1;
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            int x,y,s,t=1;
            scanf("%d%d%d",&x,&y,&s);
            while (s>=t) 
            {
                v[++n1]=x*t;
                w[n1]=y*t;
                s-=t;
                t*=2;
            }
            v[++n1]=x*s;
            w[n1]=y*s;                             //把s以2的指数分堆:1,2,4,…,2^(k-1),s-2^k+1,
        }
    for(int i=1;i<=n1;i++)
            for(int j=m;j>=v[i];j--)
               f[j]=max(f[j],f[j-v[i]]+w[i]); 
        printf("%d
    ",f[m]);
        return 0;
    }

    以上就是三种基本的背包问题(*^__^*)

     p.s.这里初学咸鱼,如有错误欢迎各位大佬们指出~

  • 相关阅读:
    DOM BOM document window 区别
    cookie、localStorage和sessionStorage区别
    css实现九宫格
    三个提升网页性能技巧
    我们为什么在移动端项目中选择jQuery而不是Zepto
    SEO优化实践操作
    查询设计分析
    SQL Server常用元数据函数
    SQL Server数学函数
    SQL Server文本和图像函数
  • 原文地址:https://www.cnblogs.com/A-S-KirigiriKyoko/p/6036368.html
Copyright © 2011-2022 走看看