1. 开心的金明 ( happy.cpp )
【问题描述】
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 N 元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的 N 元。于是,他把每件物品规定了一个重要度,分为 5 等:用整数 1~5 表示,第 5 等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。设第 j 件物品的价格为 v[j],重要度为 w[j],共选中了 k 件物品,编号依次为 j1,j2,……,jk,则所求的总和为:v[j1]*w[j1]+v[j2]*w[j2]+ …+v[jk]*w[jk]。(其中*为乘号)请你帮助金明设计一个满足要求的购物单。
【输入样式】
输入的第 1 行,为两个正整数,用一个空格隔开:
N m (其中 N(<30000)表示总钱数,m(<25)为希望购买物品的个数。)
从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 2 个非负整数
v p (其中 v 表示该物品的价格(v<=10000),p 表示该物品的重要度(1~5))
【输出样式】
输出只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000)。
【输入样例】
1000 5
800 2
400 5
300 5
400 3
200 2
【输出样例】
3900
【问题分析】
本题就是一个裸的 01 背包问题,只是原来每个物品的价值换成了价格与重要度的乘积而已。
#include <bits/stdc++.h>
using namespace std;
int v[26], w[26], bag[30001], x[26];
int main()
{
int n, weight;
cin >> weight >> n;
for (int i = 1; i <= n; i++)
{
cin >> w[i] >> x[i];
v[i] = w[i] * x[i];
}
for (int i = 1; i <= n; i++)
for (int j = weight; j >= w[i]; j--)
bag[j] = max(bag[j], bag[j - w[i]] + v[i]);
cout << bag[weight] << endl;
return 0;
}
2. 采药 ( herbs.cpp )
【问题描述】
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”如果你是辰辰,你能完成这个任务吗?
【输入样式】
第一行有两个整数 T(1 ≤ T ≤ 1000)和 M(1 ≤ M ≤ 100),用一个空格隔开,T 代表总共能够用来采药的时间,M 代表山洞里的草药的数目。接下来的 M 行每行包括两个在 1 到 100 之间(包括 1 和 100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
【输出样式】
一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
【输入样例】
70 3
71 100
69 1
1 2
【输出样例】
3
#include <bits/stdc++.h>
using namespace std;
int v[1001], w[1001], bag[1001];
int main()
{
int n, weight;
cin >> weight >> n;
for (int i = 1; i <= n; i++)
cin >> w[i] >> v[i];
for (int i = 1; i <= n; i++)
for (int j = weight; j >= w[i]; j--)
bag[j] = max(bag[j], bag[j - w[i]] + v[i]);
cout << bag[weight] << endl;
return 0;
}
3. 素数分解 ( prime.cpp )
【问题描述】
素数,又称质数,是指除 1 和其自身之外,没有其他约数的正整数。例如 2、3、5、13 都是质数,而 4、9、12、18 则不是。虽然素数不能分解成除 1 和其自身之外整数的乘积,但却可以分解成更多素数的和。你需要编程求出一个正整数最多能分解成多少个互不相同的素数的和。例如,21 = 2 + 19 是 21 的合法分解方法。21 = 2 + 3 + 5 + 11 则是分解为最多素数的方法。
【输入样式】
n (10 ≤ n ≤ 1000)。
【输出样式】
n 最多能分解成多少个不同的素数的和。
【输入样例 1】
21
【输出样例 1】
4
【输入样例 2】
128
【输出样例 2】
9
【问题分析】
此题要求对于给定的数 n 可以分解成最多几个素数之和,这里可以把每个小于等于 N 的素数看成物品的重量(不是素数不计算在物品之列),其价值为 1,这样就可以转换成对于容量为 n 的背包能装的最大价值。
不过这里与前面的 01 背包问题略有区别,前面的是求不超过背包容量 n 的最大值,也许背包没有装满的情况可以得到最大价值!但对于本题必须是正好装满,也就是说对于本题假定背包容量是 6,你不能单独把 2(素数)装入此背包中而得到价值为 1,因为 2!=6,因为此时 bag[6-2]的最大价值是 0,也就是说背包容量为 4 还没有找到解决的办法,所以就不能直接用 bag[6] = max(bag[6], bag[6 – 2] + 1),而是要先判断 bag[6 – 2] > 0,只有 bag[6 – 2] > 0 才表示这个背包是可以使用的。所以需要加一个判断,假定要求背包容量为 j 的,当前物品为 i(素数),则有:if(bag[j – i] > 0) bag[j] = max(bag[j], bag[j – i] + 1) (j >= i)
此题的解决思路为:
(1) 首先开一个有 n + 1 个元素的数组表示 0~n 容量背包所能获得的最大价值,并初始化为 0,且要把 bag[0]初始化为 1(即假定 0 是能过一个素数得到,这个是为了方便后面的处理,本质上 0 是无法通过素数得到的,所以在后期的答案处理时,把这个 1 给减去)
(2) 依次用把每一件物品(素数)装入不同容量的背包中,若当前物品重量为 i,价值为 1(i = 2 ~ n 中的素数),则对于容量为 j(j = n ~ i,从大到小)的背包有:
if(bag[j – i] > 0) bag[j] = max(bag[j], bag[j – i] + 1) (j >= i)
(3) 最终答案就是 bag[n] - 1
#include <bits/stdc++.h>
using namespace std;
int a[20001];
bool p[20001];
int main()
{
int n;
cin >> n;
for (int i = 2; i * i <= n; i++)
{
if (!p[i])
for (int j = i * i; j <= n; j += i)
p[j] = 1;
}
a[0] = 1;
for (int i = 2; i <= n; i++)
{
if (!p[i])
{
for (int j = n; j >= i; j--)
if (a[j - i]) a[j] = max(a[j], a[j - i] + 1);
}
}
if (a[n]) cout << a[n] - 1 << endl;
else cout << "0" << endl;
}