背包问题题目

U279964 取硬币

题目描述

现在有 n1+n2 种面值的硬币,其中前 n1 种为普通币,可以取任意枚,后 n2 种为纪念币,每种最多只能取 1 枚,每种硬币有一个面值,问能用多少种方法拼出 m 的面值?

输入格式

第一行包含三个整数 n1,n2,m ,分别表示普通币种类数,纪念币种类数和目标面值;

第二行 n1 个整数,第 i 种普通币的面值 a[i] 。保证 a[i] 为严格升序;

第三行 n2 个整数,第 i 种纪念币的面试 b[i] 。保证 b[i] 为严格升序。

输出格式

共一行,包含一个整数 x ,表示方法总数对 109+7 取模后的结果。

注意,不要忘记取模。

输入输出样例

输入 #1复制

3 1 5

1 2 3

1

输出 #1复制

9

说明/提示

对于 30% 的数据,保证 1≤n1+n2≤10,1≤m≤100,1≤a[i]≤100,1≤b[i]≤100 。 对于 100% 的数据,保证 1≤n1+n2≤100,1≤m≤100000,1≤a[i]≤100000,1≤b[i]≤100000 。

题解

f[i, j]

1. 集合

(1) 从前 i 种物品中选, 总v 恰好等于 j 的所有选法的集合

集合中选法的count

2. 计算集合

f[i, j]

1. 1 ~ n1 // 完全

f[i, j] += f[i - 1, j]

f[i, j] += f[i, j - vi]

2. n1 + 1 ~ n1 + n2 // 01

f[i, j] += f[i - 1, j - vi]

f[i, j] += f[i - 1, j]

3. 边界(初始化)

f[i, 0] = 1;

4. 时间 O(n^2);

代码

#include <iostream>

using namespace std;

const int MOD = 1e9 + 7, N = 110, M = 1e5 + 10;

int n1, n2, m;

int f[M];
int v[N];

int main()
{
    cin >> n1 >> n2 >> m;
    
    for (int i = 1; i <= n1; i ++ ) cin >> v[i];
    for (int i = n1 + 1; i <= n1 + n2; i ++ ) cin >> v[i];
    
    for (int i = 0; i <= n1 + n2; i ++ ) f[0] = 1; 
    
    for (int i = 1; i <= n1; i ++ )
        for (int j = v[i]; j <= m; j ++ )
            f[j] = (f[j] + f[j - v[i]]) % MOD;
    
    for (int i = n1 + 1; i <= n1 + n2; i ++ )
        for (int j = m; j >= v[i]; j -- )
            f[j] = (f[j] + f[j -v[i]]) % MOD;
        
    cout << f[m];
    
    return 0;
}

U280382 多重背包问题

题目描述

有 N 种物品和一个容量是 V 的背包。

第 i 种物品最多有 si 件,每件体积是 vi ,价值是 wi 。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。 输出最大价值。

输入格式

第一行两个整数,N,V ,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si ,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

输出格式

输出一个整数,表示最大价值。

输入输出样例

输入 #1复制

4 5

1 2 3

2 4 1

3 4 3

4 5 2

输出 #1复制

10

说明/提示

0<N,V≤100

0<vi,wi,si≤100

题解

多重背包

nn 种物品, 每种物品有多件, 每种物品的体积和价值不变

m 背包体积

f[i, j]

1. 集合

f[i, j]表示: 从前 i 种物品中选(每种物品有多件), 所选物品总体积不超过

j 的所有选法的集合

存的是集合中的某一方案, 这个方案价值max

2. 计算集合

f[i, j]

枚举第 i 种物品选 了 几个

(0i, 1i, 2i, 3i, ... ki)

f[i, j] = max(f[i - 1, j - k*vi] + k*wi, f[i, j])

f[i,j]=max(f[i-1,j] ,f[i-1,j-vi]+wi,f[i-1,j-2vi]+2wi...f[i-1,si*vi] +si *wi]);

f[i,j-vi]=max( f[i-1,j-vi] ,f[i-1,j-2vi]+ wi...f[i-1,j-(si-1)vi-vi)+(si-1)wi ,f[i-1,j-(si+1)vi]+si*wi)

3. 边界(初始化)

f[0, j] = 0;

f[i,j]

4. 时间 O(n*m*si)

代码

#include <iostream>

using namespace std;

const int N = 8 * 100, M = 1e4;

int n, m;
int f[N][M];
int v[N], w[N];

int main()
{
    cin >> n >> m;
    
    int cnt = 0;
    for (int i = 1; i <= n; i ++ )
    {
        int a, b, s;
        cin >> a >> b >> s;
        int k = 0;  // 2^k
        
        while (s > k)
        {
            if (k == 0) k ++ ;
            cnt ++ ;
            v[cnt] = k * a;
            w[cnt] = k * b;
            s -= k;
            k *= 2;
        }
        
        if (s)
        {
            cnt ++ ;
            v[cnt] = s * a;
            w[cnt] = s * b;
        }
    }
    
    n = cnt;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
        {
            f[i][j] = f[i - 1][j];
            if (j >= v[i]) f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
        }
        
    cout << f[n][m] << endl;
    
    return 0;
}

U280369 分组背包问题

题目描述

有 N 组物品和一个容量是 V 的背包。

每组物品有若干个,同一组内的物品最多只能选一个。 每件物品的体积是 vij ,价值是 wij ,其中 i 是组号,j 是组内编号。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式

第一行有两个整数 N,V ,用空格隔开,分别表示物品组数和背包容量。

接下来有 N 组数据:

每组数据第一行有一个整数 Si,表示第 i 个物品组的物数量;
每组数据接下来有 Si 行,每行有两个整数 vij, wij,用空格隔开,分别表示第 i 个物品组的第 j个物品的体积和价值;

输出格式

输出一个整数,表示最大价值。

输入输出样例

输入 #1复制

3 5

2

1 2

2 4

1

3 4

1

4 5

输出 #1复制

8

说明/提示

0<N,V≤100

0<Si≤100

0<vij,wij≤100

题解

分组背包

n , m

n 物品组

m 背包体积

每组物品里面, 有多种物品,(每种物品只有一件)

给出每组物品里, 每件物品的价值, 体积

求 从 n 组物品中选, 每组物品最 多选一件物品, 也可以不选

总体积不超过 m , 总价值最大

f[i, j]

f[i, j] = k;

1. 集合

表示: n 组物品中选, 每组物品最 多选一件物品, 也可以不选

总体积不超过 m 的所有选法的集合

(属性, 数)存的数是这个集合中的某一个方案, 这个方案总价值最大

max(价值)

2. 计算集合

f[i, j] ---> 包含的子集, 只要找 到所有子集取 Max

在这个题里面, 每个子集就是一种方案

max(1, 2, 3, 4, ...i);

f[i, j]

不选 第 i 组的物品

f[i, j] = max(f[i, j], f[i - 1, j])

选 第 i 组的物品

选第 i 组的哪一个物品

k: 第 i 组 物品中第 k 个物品

f[i, j] = max(f[i - 1, j - vik] + wik, f[i, j])

f[i,j] = max(f[i-1,j], f[i-1,j-vi1]+wi1, f[i-1,j-vi2] +wi2...f[i-1,j-vik] + wik)

f[i,j-vi1] = max(f[i-1,j-vi1],f[i-1,j-2vi1]+wi1, f[i-1,j-vi1-vi2]+wi2...f[i-1,j-vi1-vik]+wik)

3. 边界: f[0, j] = 0;

4. O(n^3)

代码

#include <iostream>

using namespace std;

const int N = 110; 

int n, m;
int f[N][N];
int s[N], v[N][N], w[N][N];

int main()
{
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++ )
    {
        cin >> s[i];
        for (int j = 1; j <= s[i]; j ++ )
            cin >> v[i][j] >> w[i][j]; 
    }
    
    for (int i = 1; i <= n; i ++ )   // 物品组 
        for (int j = 1; j <= m; j ++ )  // 体积
        {
            f[i][j] = f[i - 1][j];
            for (int k = 1; k <= s[i]; k ++ )  // 每组物品中的每个物品 
                if (v[i][k] <= j)
                    f[i][j] = max(f[i - 1][j - v[i][k]] + w[i][k], f[i][j]); 
        } 
        
    cout << f[n][m] << endl;
    
    return 0;
}

U280641 宠物小精灵之收服

题目描述

宠物小精灵是一部讲述小智和他的搭档皮卡丘一起冒险的故事。

一天,小智和皮卡丘来到了小精灵狩猎场,里面有很多珍贵的野生宠物小精灵。

小智也想收服其中的一些小精灵。

然而,野生的小精灵并不那么容易被收服。

对于每一个野生小精灵而言,小智可能需要使用很多个精灵球才能收服它,而在收服过程中,野生小精灵也会对皮卡丘造成一定的伤害(从而减少皮卡丘的体力)。

当皮卡丘的体力小于等于0时,小智就必须结束狩猎(因为他需要给皮卡丘疗伤),而使得皮卡丘体力小于等于0的野生小精灵也不会被小智收服。

当小智的精灵球用完时,狩猎也宣告结束。

我们假设小智遇到野生小精灵时有两个选择:收服它,或者离开它。

如果小智选择了收服,那么一定会扔出能够收服该小精灵的精灵球,而皮卡丘也一定会受到相应的伤害;如果选择离开它,那么小智不会损失精灵球,皮卡丘也不会损失体力。

小智的目标有两个:主要目标是收服尽可能多的野生小精灵;如果可以收服的小精灵数量一样,小智希望皮卡丘受到的伤害越小(剩余体力越大),因为他们还要继续冒险。

现在已知小智的精灵球数量和皮卡丘的初始体力,已知每一个小精灵需要的用于收服的精灵球数目和它在被收服过程中会对皮卡丘造成的伤害数目。

请问,小智该如何选择收服哪些小精灵以达到他的目标呢?

输入格式

输入数据的第一行包含三个整数:N,M,K,分别代表小智的精灵球数量、皮卡丘初始的体力值、野生小精灵的数量。

之后的K行,每一行代表一个野生小精灵,包括两个整数:收服该小精灵需要的精灵球的数量,以及收服过程中对皮卡丘造成的伤害。

输出格式

输出为一行,包含两个整数:C,R,分别表示最多收服C个小精灵,以及收服C个小精灵时皮卡丘的剩余体力值最多为R。

输入输出样例

输入 #1复制

10 100 5

7 10

2 40

2 50

1 20

4 20

输出 #1复制

3 30

输入 #2复制

10 100 5

8 110

12 10

20 10

5 200

1 110

输出 #2复制

0 100

说明/提示

0<N≤1000 , 0<M≤500 , 0<K≤100

题解

f[i, j1, j2]

1. 集合 : 从 前 i 件物品中选, 一维体积不超过 j1 (精灵球)

二维体积不超过 j2 (体力),的所有选法的集合

w[i] = 1, 小精灵的价值看成 1

max(价值)

2. 计算集合

f[i, j1, j2] v1[i], v2[i]

第 i 件物品 选 或 不选

f[i, j1, j2] = max(f[i - 1, j1, j2], f[i - 1, j1 - v1i, j2 - v2i] + 1);

3. 边界

f[0, j1, j2] = 0

4. 时间: O(n^3)

代码

#include <iostream>

using namespace std;

const int N = 1010, M = 510, K = 110;

int n, m, k;

int f[N][M];
int v1[K], v2[K];

int main()
{
    cin >> n >> m >> k;
    
    for (int i = 1; i <= k; i ++ ) cin >> v1[i] >> v2[i];
    
    for (int i = 1; i <= k; i ++)  // 物品数
        for (int j1 = n; j1 >= v1[i]; j1 -- )  // 一维体积(精灵球)
            for (int j2 = m; j2 >= v2[i]; j2 -- )
                f[j1][j2] = max(f[j1][j2], f[j1 - v1[i]][j2 - v2[i]] + 1);
        
    int res = -1, t;  // res 代表max(价值), t 消耗的二维体积(体力)
    for (int i = 0; i <= m - 1; i ++ )
        if (res < f[n][i])
        {
            res = f[n][i];
            t = i;
        }

     cout << res << ' ' << m - t << endl;
     
     return 0;
    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值