算法学习系列(五十一):背包模型(一)

引言

关于 背包问题 可以参考我之前的博客,由于基础的算法和模板已经学过了,所以现在就是开始学模型了,难点就是阅读理解、抽象模型、代码熟练度、心理素质也就是这几个难点了,其实就是多练就好了,加油!


一、采药

标签:DP、01背包问题

思路:就是一道很裸的 01 01 01 背包问题,我们把时间看作背包的容积,数量看作物品的个数,并且每件物品只能选一次,所以直接拿模板去解就行了。这里的写法跟之前的不太一样,我们发现每次的 w [ i ] , v [ i ] w[i],v[i] w[i],v[i] 其实就是该次的,不用存直接在输入的时候就一读,之后也就不用了,所以可以用如下的写法。

题目描述:

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。

为此,他想拜附近最有威望的医师为师。

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

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

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

输入格式
输入文件的第一行有两个整数 T 和 M,用一个空格隔开,T 代表总共能够用来采药的时间,M 代表山洞里的草药的数目。

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

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

数据范围
1≤T≤1000,1≤M≤100
输入样例:
70 3
71 100
69 1
1 2
输出样例:
3

示例代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y second

const int N = 110, M = 1010;

int n, m;
int f[M];

int main()
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	
	cin >> m >> n;
	for(int i = 1; i <= n; ++i)
	{
		int v, w; cin >> v >> w;
		for(int j = m; j >= v; --j)
		{
			f[j] = max(f[j], f[j-v]+w);
		}
	}
	
	cout << f[m] << endl;
	
	return 0;
}

二、装箱问题

标签:DP、01背包问题

思路:这个背包问题其实就是一个组合问题,问在满足条件的情况下,什么样的组合能使得某个值最大。这道题首先每个物品只能选择一次,所以判断是 01 01 01 背包,然后容量就是题意,问的是剩余空间最小,也就是装入的体积最大,也就是将体积看作价值了,求价值最大,也就是模板了,跟上一题一样。

题目描述:

有一个箱子容量为 V,同时有 n 个物品,每个物品有一个体积(正整数)。

要求 n 个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。

输入格式
第一行是一个整数 V,表示箱子容量。

第二行是一个整数 n,表示物品数。

接下来 n 行,每行一个正整数(不超过10000),分别表示这 n 个物品的各自体积。

输出格式
一个整数,表示箱子剩余空间。

数据范围
0<V≤20000,0<n≤30
输入样例:
24
6
8
3
12
7
9
7
输出样例:
0

示例代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y second

const int N = 2e4+10;

int n, m;
int f[N];

int main()
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	
	cin >> m >> n;
	for(int i = 1; i <= n; ++i)
	{
		int v; cin >> v;
		for(int j = m; j >= v; --j)
		{
			f[j] = max(f[j], f[j-v]+v);
		}
	}
	
	cout << m - f[m] << endl;
	
	return 0;
}

三、宠物小精灵之收服

标签:DP、01背包问题、阅读理解

思路:这道题相当于是有两个体积,然后剩余的就跟模板一样,然后注意这里的体积二不能被取满,因为取满所包含的那个数不算,当然也有可能算这个体积最后的值也没有变化,所以这里直接取 f [ V 1 ] [ V 2 − 1 ] f[V1][V2-1] f[V1][V21] 即可。然后第二问求的是最优解的情况下体积二最小是多少,那直接拿循环判断即可,详情见代码。

题目描述:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

数据范围
0<N≤1000,0<M≤500,0<K≤100
输入样例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

示例代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y second

const int N = 1010, M = 510;

int V1, V2, n;
int f[N][M];

int main()
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	
	cin >> V1 >> V2 >> n;
	for(int i = 1; i <= n; ++i)
	{
		int v1, v2; cin >> v1 >> v2;
		for(int j = V1; j >= v1; --j)
		{
			for(int k = V2; k >= v2; --k)
			{
				f[j][k] = max(f[j][k], f[j-v1][k-v2]+1);
			}
		}
	}
	
	int res = f[V1][V2-1];
	cout << res << " ";
	int k = V2 - 1;
	while(k >= 0 && f[V1][k] == res) k--;
	cout << V2 - k - 1 << endl;
	
	return 0;
}
  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lijiachang030718

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值