背包问题-练习题

目录

知识点 

一、01背包 

核心代码:

 二、完全背包

习题

一:01背包

1、装箱问题 

2、采药

3、hdu - 2602 - Bone Collector 

4、poj - 3624 - Charm Bracelet

5、hdu - 2564 - 饭卡

 二、完全背包

第一题:HDU- 1114 - Piggy-Bank


初学背包问题,综合了很多博客的文章,总结了一下知识点和基础的习题。 

知识点 

一、01背包 

有n件物品(每种物品都只有一件),

w[i]表示物品的重量,v[i]表示物品的价值,

现有一个容量为V的背包,

应该如何选物品使得书包内装的物品的value之和最大呢?

例:有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,每件物品数量只有一个,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?  

迭代数组:

nameweightvalue012345678910
a2606666666666
b2306699999999
c6500000099111114
d5400000499111114
e4600006699111115

核心代码:

① 二维数组:时间复杂度和空间复杂度都是O(n*V)

对于第 i 个物品,有选和不选两种情况

dp[\ i \ ][\ j\ ]表示在容量为 j 的情况下选取 i 个物品的最大 value 值。

 核心代码:

for (i = 1; i <= n; i++)
    for (j = w[i]; j <= V; j++)
        dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);

② 一维数组:时间复杂度O(n*V),空间复杂度O(V)

dp[\ j\ ] 表示 背包容量 为 j 时,能够包容的最大 value 值。

核心代码:

for (i = 1; i <= n; i++)
    for (j = V; j >= w[i]; j--)
        dp[j] = max(dp[j], dp[j - w[i]] + v[i]);

 

 

学习博客:

01背包问题 二维解法 及 输出路径:https://blog.csdn.net/weixin_40372617/article/details/102872786

一维01背包:为什么可行?为什么逆序?https://blog.csdn.net/weixin_40372617/article/details/102872786 

                                                                 https://blog.csdn.net/Cassie_zkq/article/details/82944880

 二、完全背包

有n件物品(每种物品有无数件),

w[i]表示物品的重量,v[i]表示物品的价值,

现有一个容量为V的背包,

应该如何选物品使得书包内装的物品的value之和最大呢?

例:有编号分别为a,b,c,d的四件物品,它们的重量分别是2,3,4,7,它们的价值分别是1,3,5,9,每件物品数量无限个,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?

迭代数组:

nameweightvalue012345678910
a2100112233445
b3300133466799
c4500135568101011
d7900135569101012

核心代码:

for (i = 1; i <= n; i++)
    for (j = w[i]; j <= V; j++)
        dp[j] = max(dp[j], dp[j - w[i]] + v[i]);

学习博客:动态规划之背包问题(二):完全背包问题:https://blog.csdn.net/siyu1993/article/details/52858940 

                  背包问题详解:01背包、完全背包、多重背包:https://blog.csdn.net/na_beginning/article/details/62884939

三、多重背包 

有n件物品(每种物品有 num[i] 件),

w[i]表示物品的重量,v[i]表示物品的价值,

现有一个容量为V的背包,

应该如何选物品使得书包内装的物品的value之和最大呢?

例:有编号分别为a,b,c的三件物品,它们的重量分别是1,2,2,它们的价值分别是6,10,20,他们的数目分别是10,5,2,现在给你个承重为 8 的背包,如何让背包里装入的物品具有最大的价值总和? 

解题思路:

作为一个新问题考虑,由于每个物品多了数目限制,因此初始化和递推公式都需要更改一下。初始化时,只考虑一件物品a时,f[1][j] = min{num[1], j/weight[1]}。 计算考虑i件物品承重限制为y时最大价值f[i][y]时,递推公式考虑两种情况,要么第 i 件物品一件也不放,就是f[i-1][y], 要么第 i 件物品放 k 件,其中 1 <= k <= (y/weight[i]),考虑这一共 k+1 种情况取其中的最大价值即为f[i][y]的值,即f[i][y] = max{f[i-1][y], (f[i-1][y-k*weight[i]]+k*value[i])}。 这里为什么不能像完全背包一样直接考虑f[i][y-weight[i]]+value[i]呢?因为这样不容易判断第 i 件物品的个数是否超过限制数量 num[i]。

nameweightvaluenum012345678
a16100612182430364248
b21050612182430364248
c22020620264046525864

 

习题

一:01背包

1、装箱问题 

模板题,难度:★☆☆☆☆

时间限制 : 1.000 sec  内存限制 : 128 MB

【问题描述】
有一个箱子的容量为V(V为正整数,且满足0≤V≤20000),同时有n件物品(0的体积值为正整数。
要求从n件物品中,选取若干装入箱内,使箱子的剩余空间最小。
输入:1行整数,第1个数表示箱子的容量,第2个数表示有n件物品,后面n个数分别表示这n件
物品各自的体积。
输出:1个整数,表示箱子剩余空间。
【输入输出样例】
输入:

24 6 8 3 12 7 9 7

输出:

0
#include <bits/stdc++.h>
using namespace std;
 
int main()
{
    int v, n, a[200005], dp[200005];
    cin >> v >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];

    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= n; i++) //遍历物品
        for (int j = v; j >= a[i]; j--) //倒序遍历箱子容量
            dp[j] = max(dp[j], dp[j - a[i]] + a[i]);

    int ans=v-dp[v];
    cout<<ans<<endl;
    return 0;
}

2、采药

模板题,难度:★☆☆☆☆

时间限制 : 1.000 sec  内存限制 : 128 MB

【题目描述】

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。

医师为了判断他的资质,给他出了一个难题。

医 师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间, 在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

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

 【输入 】

第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,

T代表总共能够用来采药的时间,M代表山洞里的草药的数目。

接下来的M行每行包括两个在1到100之间(包括1和100)的整 数,分别表示采摘某株草药的时间和这株草药的价值。

【输出】

一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

【样例输入】

70 3
71 100
69 1
1 2

【样例输出】

3

【数据规模】

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

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

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
int main()
{
    int t, m, time[1005], value[1005], dp[1005] = {0};
    cin >> t >> m;
    for (int i = 1; i <= m; i++)
        cin >> time[i] >> value[i];

    for (int i = 1; i <= m; i++)
        for (int j = t; j >= time[i]; j--)
            dp[j] = max(dp[j], dp[j - time[i]] + value[i]);

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

3、hdu - 2602 - Bone Collector 

模板题,难度:★☆☆☆☆

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)


Problem Description
Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …
The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?

 
 
Input
The first line contain a integer T , the number of cases.
Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.
 
 
Output
One integer per line representing the maximum of the total value (this number will be less than 231).
 
 
Sample Input
1 
5 10 
1 2 3 4 5 
5 4 3 2 1

Sample Output

14
#include <bits/stdc++.h>
using namespace std;

int main()
{
    int T, n, v;
    int volume[1005], value[1005], dp[1005];
    cin >> T;
    while (T--)
    {
        memset(dp, 0, sizeof(dp));
        cin >> n >> v;
        for (int i = 1; i <= n; i++)
            cin >> value[i];
        for (int i = 1; i <= n; i++)
            cin >> volume[i];
        for (int i = 1; i <= n; i++)
        {
            for (int j = v; j >= volume[i]; j--)
            {
                dp[j] = max(dp[j - 1], dp[j - volume[i]] + value[i]);
            }
        }
        cout << dp[v] << endl;
    }
    //system("pause");
    return 0;
}

4、poj - 3624 - Charm Bracelet

模板题,难度:★☆☆☆☆

Time Limit: 1000MS               Memory Limit: 65536K

 

【Description】

Bessie has gone to the mall's jewelry store and spies a charm bracelet. Of course, she'd like to fill it with the best charms possible from the N (1 ≤ N ≤ 3,402) available charms. Each charm i in the supplied list has a weight Wi (1 ≤ Wi ≤ 400), a 'desirability' factor Di (1 ≤ Di ≤ 100), and can be used at most once. Bessie can only support a charm bracelet whose weight is no more than M (1 ≤ M ≤ 12,880).

Given that weight limit as a constraint and a list of the charms with their weights and desirability rating, deduce the maximum possible sum of ratings.

【Input】

* Line 1: Two space-separated integers: N and M
* Lines 2..N+1: Line i+1 describes charm i with two space-separated integers: Wi and Di

【Output】

* Line 1: A single integer that is the greatest sum of charm desirabilities that can be achieved given the weight constraints

Sample Input

4 6
1 4
2 6
3 12
2 7

Sample Output

23
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    int T, n, m;
    int w[20005], d[20005], dp[20005];

    while (cin >> n >> m)
    {
        memset(dp, 0, sizeof(dp));
        for (int i = 1; i <= n; i++)
            cin >> w[i] >> d[i];

        for (int i = 1; i <= n; i++)
            for (int j = m; j >= w[i]; j--)
                dp[j] = max(dp[j], dp[j - w[i]] + d[i]);

        cout << dp[m] << endl;
    }
    //system("pause");
    return 0;
}

5、hdu - 2564 - 饭卡

难度:★★☆☆☆

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Problem Description

电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。

Input
多组数据。对于每组数据:
第一行为正整数n,表示菜的数量。n<=1000。
第二行包括n个正整数,表示每种菜的价格。价格不超过50。
第三行包括一个正整数m,表示卡上的余额。m<=1000。

n=0表示数据结束。
 
Output
对于每组输入,输出一行,包含一个整数,表示卡上可能的最小余额。
 
Sample Input
1
50
5
10
1 2 3 2 1 1 2 3 2 1
50
0

Sample Output

-45 
32

题目思路

将该问题转化成0-1背包问题,由于每个菜只能购买一次,而且每次买菜的时候饭卡余额 m 要大于等于5,因此先利用0-1背包算出 m - 5 的钱最多能买多少菜,然后拿最后的5块钱买最贵的菜,才能使饭卡余额达到最少。而且这道题还要注意的一点是在进行0-1背包之前要将所有的菜从小到大排一下序,因为要使饭卡余额最少必须要用最后5块钱买最贵的菜,而每个菜只能购买一次,因此要在0-1背包之前sort排序,防止最贵的菜购买了多次。 

学习于:https://blog.csdn.net/qq_41181772/article/details/88650132 

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
int main()
{
    int n, balance, value[1005], dp[1005];
    while (cin >> n, n)
    {
        for (int i = 1; i <= n; i++)
            cin >> value[i];
        cin >> balance;
        sort(value + 1, value + 1 + n);
        memset(dp, 0, sizeof(dp));
        if (balance < 5)
        {
            cout << balance << endl;
            continue;
        }

        for (int i = 1; i < n; i++)
            for (int j = balance - 5; j >= value[i]; j--)
                dp[j] = max(dp[j], dp[j - value[i]] + value[i]);

        cout << balance - dp[balance - 5] - value[n] << endl;
    }
    return 0;
}

 二、完全背包

第一题:HDU- 1114 - Piggy-Bank

完全背包的模板题,难度:★★☆☆☆

【题意】

首先第一行输入测试次数,然后每个测试用例第一行:空存钱罐的重量 和 装满钱时候存钱罐的重量,然后第二行输入现有的钱的种类n,后面就是每种钱的价值和钱的重量。要求用存钱罐中钱的重量来找到存钱罐中的最低金额,如果可以找到那么打印“ The minimum amount of money in the piggy-bank is X.”,其中X是使用给定总重量的硬币可以达到的最低金额。如果不能准确地达到重量,This is impossible.”。 

【样例输入】

3
10 110
2
1 1
30 50
10 110
2
1 1
50 30
1 6
2
10 3
20 4

【样例输出】

The minimum amount of money in the piggy-bank is 60.
The minimum amount of money in the piggy-bank is 100.
This is impossible.

参考博客:https://blog.csdn.net/yc_cy1999/article/details/105129923

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int main()
{
    ios::sync_with_stdio(false);

    int T, e, f, n, p[505], w[505], dp[10005];
    cin >> T;
    while (T--)
    {
        memset(dp, inf, sizeof(dp));
        dp[0] = 0;
        cin >> e >> f;
        cin >> n;
        for (int i = 1; i <= n; i++)
            cin >> p[i] >> w[i];

        for (int i = 1; i <= n; i++)
            for (int j = w[i]; j <= f - e; j++)
                dp[j] = min(dp[j], dp[j - w[i]] + p[i]);

        if (dp[f - e] == inf)
            cout << "This is impossible.\n";
        else
            cout << "The minimum amount of money in the piggy-bank is " << dp[f - e] << ".\n";
    }
    //system("pause");
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值