题解 P2722 【总分 Score Inflation】

· · 题解

想弄懂完全背包题,当然是要先看01背包喽!作为一个刚刚学会背包一个星期的蒟蒻,我决定把我弄懂01背包和完全背包的过程分享给更多蒟蒻。

01背包

题目描述

一个旅行者有一个最多能装 M 公斤的背包,现在有 n 件物品,它们的重量分别是W1,W2,...,Wn,它们的价值分别为C1,C2,...,Cn,求旅行者能获得最大总价值。

【输入】

第一行:两个整数,M (背包容量,M≤200)和N(物品数量,N≤30)

第2..N+1 行:每行二个整数Wi,Ci,表示每个物品的重量和价值。

【输出】

仅一行,一个数,表示最大总价值。

下面是01背包模板代码,最下面有样例数据

    #include<iostream>
    using namespace std;
    const int maxm=201,maxn=31;
    int m,n;
    int w[maxn],c[maxn];
    int f[maxn][maxm];//表示前i个物品在容量为v下可以获得的
    最大总价值 
    int main()
    {
      cin>>m>>n;//背包容量m和物品数量n 
      for(int i=1;i<=n;i++){
          cin>>w[i]>>c[i];//输入每个物品的质量和价值 
      }
      for(int i=1;i<=n;i++){
          for(int v=m;v>0;v--){
              if(w[i]<=v){
                 f[i][v]=max(f[i-1][v],f[i-1][v-w[i]]+c[i]);
              }
    //f[i][v]表示前i件物品,总体积(质量)不超过v的最优价值 
              else{
                  f[i][v]=f[i-1][v];
              }
          }//个人习惯,每个循环都带括号,方便添加代码
       }
       cout<<f[n][m];//f[n][m]为最优解 
       return 0;
    }

f[i-1][v-w[i]]这东西代表的就是在v的空间下,放入w[i](也就 是第i件物品)后再放之前的物品获得的价值。[v-w[i]]就是现在 的体积减掉该物品体积后剩下的,就是可以前i件物品留下的, 在之前的计算中已经求得了前i件物品可以得到的价值.

它具有自动检索一般的功能,比如样例中我们在i=4时,它根据 [v-w[i]]找到了此时除了它剩下的空间放前面物品最多可以获得3 的价值,再加上它的9的价值得到12。同时它还和只放3个物品进行比较,如果第四个物品价值过小,程序将会选择不拿4。

当我把 输入数据第四个物品和第三个物品的价值改为7时,发现f[3][10] 就已经达到了最大值11,程序在i=4时没有更改结果

以下是通过文件操作打出来的数据

输入:

10 4

2 1

3 3

4 5

7 9

输出: 12

测试输出:

(每段数据有10个,第i段数据代表i等于几时的f[i][1..10],每次的第一个数据代表本次操作时的 v)

10 1

9 1

8 1

7 1

6 1

5 1

4 1

3 1

2 1

1 0

10 4

9 4

8 4

7 4

6 4

5 4

4 3

3 3

2 1

1 0

10 9

9 9

8 8

7 8

6 6

5 5

4 5

3 3

2 1

1 0

10 12

9 10

8 9

7 9

6 6

5 5

4 5

3 3

2 1

1 0

下面是用一维数组实现01背包

(用的本题数据和字母)但本题不是01背包题(因为每个种类中你可以选择若干道题,而不是只能做一道或不做),所以下面代码无法通过

    #include<iostream>
    using namespace std;
    int x,y;
    int t[10002],p[10002];
    int f[10002];
    int main()
    {
        int m,n;
        cin>>m>>n;
        for(int i=1;i<=n;i++){
            cin>>p[i]>>t[i];
        }
        for(int i=1;i<=n;i++){
            for(int t1=m;t1>=t[i];t1--){
                f[t1]=max(f[t1],f[t1-t[i]]+p[i]);
             }
        }
        cout<<f[m];
        return 0;
    }

这里我们可以看到,它省去了数组中用来表示前i个物品(题目)的一维,因为这不重要。我们不需要管它是在前几个物品(题目)中在容量v(时间t)下得到的最大价值(最高分数),我们只需要知道在容量v下可以得到的最大价值。所以现在的f[100002]就是代表在多少容量(时间)下可以得到的最高价值(分数)。其余都和上面的背包模板一样。

好,现在就是完全背包了

下面是AC代码

#include<iostream>
    using namespace std;
    int x,y;
    int t[10002],p[10002];
    int f[10002];
    int main()
    {
        int m,n;
        cin>>m>>n;
           for(int i=1;i<=n;i++){
              cin>>p[i]>>t[i];
           }
        for(int i=1;i<=n;i++){
            for(int t1=t[i];t1<=m;t1++){//注意这一行
                f[t1]=max(f[t1],f[t1-t[i]]+p[i]);
            }
        }
        cout<<f[m];
        return 0;
    }

也许没有我的“注意这一行”,你可能找不出来它和上面的01背包代码有什么区别。这也正说明了完全背包和01背包的相似。他们的差别只有第二重for循环的差别。

在01背包中,t1从m一直减到t【i】,保证了每个物品在一个t1中只会被放一次。 而完全背包中,从t1一直加到m,使在可以放下的情况下尽量多地放。