【SSL】2021-08-19 1045.采药

原题网址

由于某些原因,这个网址会进不去…

题目描述

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

格式

输入格式

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

输出格式

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

样例

输入样例

70 3
71 100
69 1
1 2

输出样例

3

提示

对于 30 % 30\% 30%的数据, M ≤ 10 M\le10 M10
对于全部的数据, M ≤ 100 M\le100 M100

解题思路

这题是 D P DP DP(动态规划) 背包问题的一个经典例题。而像基础的背包问题解决办法都是依照下图来的:
1045-1
这个图是在输入为

10 4
5 8 
3 6
4 7
2 5

的时候出现的结果。动态规划数组 f [ i ] [ v ] f[i][v] f[i][v]的数字表示前 i i i种草药在时间为 v v v时可采集的最大草药数。但是上图有错,正确应如下图:
1045-2
上图的红框内就是错误的地方,蓝色的“ 8 8 8”是正确该填的数字。废话少说,我们来讲讲这个图是怎么来的:我们举个栗子,上图的绿框内数字“ 7 7 7”是这么求来的:首先,考虑不选择采集当前第 i i i种草药(现在为 3 3 3)的情况,也就是 f [ i − 1 ] [ v ] f[i-1][v] f[i1][v];其次,考虑时间足够且选择当前第 i i i种草药(现在为 3 3 3)的情况,也就是要把时间分成两部分,一部分是为了采摘当前种草药(现在为 4 4 4)的,另一部分是为了采摘之前的最大草药数(现在为 6 6 6),则为 f [ i − 1 ] [ v − t [ i ] ] + w [ i ] f[i-1][v-t[i]]+w[i] f[i1][vt[i]]+w[i](其中 t [ i ] t[i] t[i]表示采摘第 i i i种草药所需的时间, w [ i ] w[i] w[i]表示第 i i i种草药的价值)。

Code

顺推法

#include<iostream>
#include<iomanip>
#include<istream>
#include<ostream>
#include<ios>
#include<set>
#include<fstream>
#include<sstream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<map>
#include<queue>
#define maxn 1000
using namespace std;
int t, m, q[maxn + 1], w[maxn + 1], s[maxn + 1][maxn + 1];

void init()
{
	cin>>t>>m;
	for (int i = 1; i <= m; i ++) cin>>q[i]>>w[i];
}

void comp()
{
	for (int i = 1; i <= m; i ++) for (int j = 1; j <= t; j ++) // DP(动态规划)循环
	{
		if (q[i] <= j) s[i][j] = max(s[i - 1][j], s[i - 1][j - q[i]] + w[i]);
		else s[i][j] = s[i - 1][j]; // 顺推,使用二维数组
	}
}

void oput()
{
	cout<<s[m][t]; // 输出结果
}

int main()
{
	init();
	comp();
	oput();
	return 0;
}

逆推法

#include<iostream>
#include<iomanip>
#include<istream>
#include<ostream>
#include<ios>
#include<set>
#include<fstream>
#include<sstream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<map>
#include<queue>
#define maxn 1000
using namespace std;
int t, m, q[maxn + 1], w[maxn + 1], s[maxn + 1];

void init()
{
	cin>>t>>m;
	for (int i = 1; i <= m; i ++) cin>>q[i]>>w[i];
}

void comp()
{
	for (int i = 1; i <= m; i ++) for (int j = t; j >= 1; j --) // DP(动态规划)循环
	{
		if (q[i] <= j) s[j] = max(s[j], s[j - q[i]] + w[i]); // 逆推,使用一维数组
	}
}

void oput()
{
	cout<<s[t]; // 输出结果
}

int main()
{
	init();
	comp();
	oput();
	return 0;
}

顺推+编号

#include<iostream>
#include<iomanip>
#include<istream>
#include<ostream>
#include<ios>
#include<set>
#include<fstream>
#include<sstream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<map>
#include<queue>
#define maxn 1000
using namespace std;
int t, m, q[maxn + 1], w[maxn + 1], s[maxn + 1][maxn + 1], e[maxn + 1];

void init()
{
	cin>>t>>m;
	for (int i = 1; i <= m; i ++) cin>>q[i]>>w[i];
}

void comp()
{
	for (int i = 1; i <= m; i ++) for (int j = 1; j <= t; j ++) // DP(动态规划)循环
	{
		if (q[i] <= j) s[i][j] = max(s[i - 1][j], s[i - 1][j - q[i]] + w[i]);
		else s[i][j] = s[i - 1][j]; // 顺推,使用二维数组
	}
	for (int i = m; i >= 1; i --)
	{
		if (s[i][t]!=s[i-1][t]) // 说明当前选择了新的草药
		{
			e[i] = 1;
		}
	}
}

void oput()
{
	cout<<s[m][t];
	cout<<endl;
	for (int i = 1; i <= m; i ++) if (e[i]) cout<<i<<" ";
}

int main()
{
	init();
	comp();
	oput();
	return 0;
}

顺推+滚动

滚动数组

#include<iostream>
#include<iomanip>
#include<istream>
#include<ostream>
#include<ios>
#include<set>
#include<fstream>
#include<sstream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<map>
#include<queue>
#include<vector>
#define maxn 1000000
using namespace std;
int n, m, a[maxn + 1], b[maxn + 1], t[maxn + 1], s[maxn + 1];

void init()
{
	cin>>n>>m;
	for (int i = 1; i <= m; i ++) cin>>t[i]>>s[i];
}

void dp()
{
	for (int i = 1; i <= m; i ++) // DP(动态规划)循环
	{
		memcpy(b, a, sizeof(a)); // 滚动数组
		for (int j = t[i]; j <= n; j ++) a[j] = max(b[j], b[j - t[i]] + s[i]);
	}
}

void oput()
{
	cout<<a[n]; // 输出结果
}

int main()
{
	init();
	dp();
	oput();
	return 0;
}

大功告成 ∼ \sim

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值