原题网址
题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。” 如果你是辰辰,你能完成这个任务吗?
格式
输入格式
输入的第一行有两个整数 T ( 1 ≤ T ≤ 1000 ) T(1\le T\le1000) T(1≤T≤1000)和 M ( 1 ≤ M ≤ 100 ) M(1\le M\le100) M(1≤M≤100),用一个空格隔开, 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
M≤10;
对于全部的数据,
M
≤
100
M\le100
M≤100。
解题思路
这题是
D
P
DP
DP(动态规划) 背包问题的一个经典例题。而像基础的背包问题解决办法都是依照下图来的:
这个图是在输入为
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时可采集的最大草药数。但是上图有错,正确应如下图:
上图的红框内就是错误的地方,蓝色的“
8
8
8”是正确该填的数字。废话少说,我们来讲讲这个图是怎么来的:我们举个栗子,上图的绿框内数字“
7
7
7”是这么求来的:首先,考虑不选择采集当前第
i
i
i种草药(现在为
3
3
3)的情况,也就是
f
[
i
−
1
]
[
v
]
f[i-1][v]
f[i−1][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[i−1][v−t[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 ∼