因为本文章是习题练习,所有不会有具体讲解。如想看具体的思路讲解,请在我的博客中找到动态规划算法详解, 或者访问
问题 A: 【一本通基础DP背包】【例9.11】01背包问题
[题目描述]
一个旅行者有一个最多能装 M 公斤的背包,现在有 n 件物品,它们的重量分别是W_1,W_2,...,W_n,它们的价值分别为C_1,C_2,...,C_n,求旅行者能获得最大总价值。
输入
第一行:两个整数,N(物品数量n<=100)和M(背包容量,M<=10000);
第2..N+1行:每行二个整数W_i,C_i,表示每个物品的重量和价值。
输出
仅一行,一个数,表示最大总价值。
样例输入
4 10 2 1 3 3 4 5 7 9
样例输出
12
#include "bits/stdc++.h"
using namespace std;
int dp[120][10001], c[110], w[110], n, v;
int main()
{
cin >> n >> v;
for(int i = 1; i <= n; i++)
{
cin >> c[i] >> w[i];
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= v; j++)
{
if(j < c[i])
dp[i][j] = dp[i - 1][j];
else
{
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - c[i]] + w[i]);
}
}
}
cout << dp[n][v] << endl;
return 0;
}
问题 B: 【一本通基础DP背包】【例9.12】完全背包问题
[题目描述]
设有n种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为M,今从n种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于M,而价值的和为最大。
输入
第一行:两个整数,N(物品数量,N<=100)和M(背包容量,M≤10000);
第2..N+1行:每行二个整数Wi,Ci,表示每个物品的重量和价值。
输出
仅一行,一个数,表示最大总价值。
样例输入
4 10 2 1 3 3 4 5 7 9
样例输出
max=12
#include <bits/stdc++.h>
using namespace std;
int dp[10010], i, j, n, m, weight[110], value[110];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
cin >> weight[i] >> value[i];
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
if(j >= weight[i])
{
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
}
cout << "max=" << dp[m] << endl;
return 0;
}
问题 C: 【一本通基础DP背包】【例9.13】 庆功会
[题目描述]
同学们在期中考中获得了团体第一名,班主任吴老师决定开一场庆功会。为此拨款买奖品犒劳同学们,期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。
输入
第一行二个数n(n<=500),m(m<=6000),其中n代表希望购买的奖品的种数,m表示拨款的金额,
接下来n行,每行3个数,v、w、s,分别表示第I种物品的价格、价值(价格与价值是不同的概念)和购买的数量(只能买0件------s件),其中v<=100,w<=1000,s<=10
输出
一个数,表示此次购买获得的最大价值。
样例输入
5 1000 8 20 4 40 50 9 30 50 7 40 30 6 20 20 1
样例输出
1080
#include <bits/stdc++.h>
using namespace std;
int v[510], w[510], s[510];
int dp[6010];
int n, m;
int maxx(int x, int y)
{
if(x > y)
return x;
else
return y;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
{
scanf("%d%d%d", &v[i], &w[i], &s[i]);
}
for(int i = 1; i <= n; i++)
{
for(int j = m; j >= 0; j--)
{
for(int k = 0; k <= s[i]; k++)
{
if(j - k * v[i] < 0)
{
break;
}
dp[j] = maxx(dp[j], dp[j - k * v[i]] + k * w[i]);
}
}
}
printf("%d\n", dp[m]);
return 0;
}
问题 D: 【一本通基础DP背包】【例9.14】 混合背包
[题目描述]
一个旅行者有一个最多能用V公斤的背包,现在有n件物品,它们的重量分别是W1,W2,...,Wn,它们的价值分别为C1,C2,...,Cn.有的物品只可以取一次,有的物品可以取无限次,有的物品可以取得次数有一个上限,求将哪些物品装入背包这些物品的费用总和不超过背包容量且价值总和最大。
输入
第1行:两个整数V(背包容量,V<=200),n(n<=30,物品数量)
第2至第n行:每行3个整数wi,ci,pi,wi为物品重量,ci为价值,pi如果为0,则表明次物品可以取无数次,如果为其他数字,则次物品最多取的件数为pi
输出
一行,一个数,表示最大总价值
样例输入
10 3 2 1 0 3 3 1 4 5 4
样例输出
11
#include <bits/stdc++.h>
using namespace std;
int v, n;
int w[40], c[40], p[40];
int dp[205];
int main()
{
scanf("%d%d", &v, &n);
for(int i = 1; i <= n; i++)
{
cin >> w[i] >> c[i] >> p[i];
}
for(int i = 1; i <= n; i++)
{
if(!p[i])
{
for(int j = w[i]; j <= v; j++)
{
dp[j] = max(dp[j], dp[j - w[i]] + c[i]);
}
}
else
{
for(int j = v; j >= w[i]; j--)
{
for(int k = 0; k <= p[i]; k++)
{
if(j >= k * w[i])
{
dp[j] = max(dp[j], dp[j - k * w[i]] + k * c[i]);
}
}
}
}
}
cout << dp[v];
return 0;
}
问题 E: 【一本通基础DP背包】【例9.15】 潜水员的问题
[题目描述]
【问题描述】
一个潜水员在潜水时使用一种特殊的装置:一个有两个容器的气筒。一个容器中装的是氧气,另一个容器中装氮气。潜水员需要携带的氧气和氮气量依赖于潜水的时间和深度。潜水员有一系列的气筒,用来在不同的情况下携带。每个气筒可以用这样几个量来描述:气筒的质量,气筒中所能容纳的氧气量,以及可以容纳的氮气量。为了能完成最近的一个任务,潜水员需要一定量的氧气和氮气。潜水员有一系列准备好的气筒。他希望能携带总质量尽可能小的气筒下水。现在请你帮他计算一下至少要携带多少质量的气筒下水才能完成这个任务。
【示例说明】
潜水员有以下 5 个气筒。每个气筒用三个整数来描述:气筒所能容纳的氧气的量,氮气的量和气筒的质量:
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
如果这次任务中潜水员需要携带 5 升 氧气, 60 升 氮气。那么他至少要携带总质量为 249 的气筒下水(样例中的第一个和第二个气筒或者第四个和第五个气筒)。
【任务】
写一个程序:
• 从输入文件中读入完成任务所需要的氧气和氮气量以及气筒的个数和对每个气筒的描述。
• 计算潜水员完成任务至少需要携带的多少质量的气筒。
• 将结果写入输出文件中。
注意:题目中给出的气筒总是能够容纳足够多的气体使得潜水员能完成任务。
【输入格式】
在文件的第一行中有两个整数 t 和 a ,分别描述完成任务所需的氧气和氮气量。( 1 ≤ t ≤ 21 , 1 ≤ a ≤ 79 )。第二行中有一个整数 n ,表示气筒的个数。( 1 ≤ n ≤ 1000 )。以后 n 行中,每行有三个整数 t i , a i , w i , t i 表示第 i 个气筒所能容纳的氧气量, a i 表示第 i 个气筒所能容纳的氮气量, w i 表示气筒 i 的质量。( 1 ≤ a i ≤ 21 , 1 ≤ t i ≤ 79 , 1 ≤ w i ≤ 800 )。
【输出格式】
输出文件只有一行,这行中包含一个整数,表示最少需要携带的多少质量的气筒来完成改任务)。
【样例输入】
ple.in
5 60
5
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
【样例输出】
ple.out
249
#include <bits/stdc++.h>
using namespace std;
int t, a, n;
int x[1001], y[1001], z[1001];
int dp[50][150];
int main()
{
memset(dp, 127, sizeof(dp));
dp[0][0] = 0;
scanf("%d%d", &t, &a);
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%d%d%d", &x[i], &y[i], &z[i]);
}
// for(int i = 1; i <= n; i++)
// {
// cin >> x[i] >> y[i] >> z[i];
// }
for(int i = 1; i <= n; i++)
{
for(int j = t; j >= 0; j--)
{
for(int k = a; k >= 0; k--)
{
int t1 = j + x[i];
int t2 = k + y[i];
if(t1 > t)
{
t1 = t;
}
if(t2 > a)
{
t2 = a;
}
if(dp[t1][t2] > dp[j][k] + z[i])
{
dp[t1][t2] = dp[j][k] + z[i];
}
}
}
}
printf("%d\n", dp[t][a]);
//cout << t << " " << a << endl;
// for(int i = 1; i <= t; i++)
// {
// for(int j = 1; j <= a; j++)
// {
// cout << dp[i][j] << " ";
// }
// cout << endl;
// }
return 0;
}
问题 F: 【一本通基础DP背包】【例9.16】 分组背包
[题目描述]
一个旅行者有一个最多能用V公斤的背包,现在有n件物品,它们的重量分别是W1,W2,...,Wn,它们的价值分别为C1,C2,...,Cn.这些物品被划分为若干组,每组中的物品相互冲突,最多选一件。求解将哪些物品装入背包可使被装入物品的容量不超过V,且价值总和最大。
输入
第1行:三个整数V(背包容量,V<=200),n(n<=30,物品数量)和T(最大组号,T<=10)
第2至第n行:每行3个整数wi,ci,pi,wi为物品重量,ci为价值,pi为组号
输出
一行,一个数,表示最大总价值
样例输入
10 6 3 2 1 1 3 3 1 4 8 2 6 9 2 2 8 3 3 9 3
样例输出
20
#include <bits/stdc++.h>
using namespace std;
int dp[300];
int w[100];
int c[100];
int a[100][100];
int main()
{
int v, n, zushu, p;
//v是背包容量, n是物品数量, p为组号
scanf("%d%d%d", &v, &n, &zushu);
for(int i = 1; i <= n; i++)
{
scanf("%d%d%d", &w[i], &c[i], &p);
a[p][++a[p][0]] = i;
}
for(int i = 1; i <= zushu; i++)
{
for(int j = v; j >= 0; j--)
{
for(int k = 1; k <= a[i][0]; k++)
{
if(dp[j] < dp[j - w[a[i][k]]] + c[a[i][k]] && j >= w[a[i][k]])
{
dp[j] = dp[j - w[a[i][k]]] + c[a[i][k]];
}
}
}
}
printf("%d", dp[v]);
return 0;
}
问题 G: 【一本通基础DP背包】【例9.17】货币系统
[题目描述]
母牛们不但创建了他们自己的政府而且选择了建立了自己的货币系统。
[In their own rebellious way],,他们对货币的数值感到好奇。
传统地,一个货币系统是由1,5,10,20 或 25,50, 和 100的单位面值组成的。
母牛想知道有多少种不同的方法来用货币系统中的货币来构造一个确定的数值。
举例来说, 使用一个货币系统 {1,2,5,10,...}产生 18单位面值的一些可能的方法是:18x1, 9x2, 8x2+2x1, 3x5+2+1,等等其它。
写一个程序来计算有多少种方法用给定的货币系统来构造一定数量的面值。
保证总数将会适合long long (C/C++) 和 Int64 (Free Pascal)。
输入
货币系统中货币的种类数目是 V 。 (1<= V<=25)
要构造的数量钱是 N 。 (1<= N<=10,000)
第 1 行: | 二整数, V 和 N |
第 2 ..V+1行: | 可用的货币 V 个整数 (每行一个 每行没有其它的数)。 |
输出
单独的一行包含那个可能的构造的方案数。
样例输入
3 10 1 2 5
样例输出
10
#include <bits/stdc++.h>
using namespace std;
long long int dp[10010];
long long int v, n;
long long int a[35];
int main()
{
scanf("%lld%lld", &v, &n);
for(int i = 1; i <= v; i++)
{
scanf("%lld", &a[i]);
}
dp[0] = 1;
for(int i = 1; i <= v; i++)
{
for(int j = a[i]; j <= n; j++)
{
dp[j] = dp[j] + dp[j - a[i]];
}
}
printf("%lld", dp[n]);
return 0;
}
问题 H: 【一本通基础DP背包】NOIP2005 采药
[题目描述]
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
输入
输入文件medic.in的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
输出
输出文件medic.out包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
样例输入
70 3 71 100 69 1 1 2
样例输出
3
#include <bits/stdc++.h>
using namespace std;
int dp[10010];
int money[101];
int shijian[101];
int t, shumu;
int main()
{
scanf("%d%d", &t, &shumu);
for(int i = 1; i <= shumu; i++)
{
scanf("%d%d", &shijian[i], &money[i]);
}
for(int i = 1; i <= shumu; i++)
{
for(int j = t; j >= shijian[i]; j--)
{
if(dp[j] < dp[j - shijian[i]] + money[i])
{
dp[j] = dp[j - shijian[i]] + money[i];
}
}
}
printf("%d", dp[t]);
return 0;
}
问题 I: 【一本通基础DP背包】数字组合
[题目描述]
有n个正整数,找出其中和为t(t也是正整数)的可能的组合方式。如:
n=5,5个数分别为1,2,3,4,5,t=5;
那么可能的组合有5=1+4和5=2+3和5=5三种组合方式。
输入
输入的第一行是两个正整数n和t,用空格隔开,其中1≤n≤20,表示正整数的个数,t为要求的和(1≤t≤1000);
接下来的一行是n个正整数,用空格隔开。
输出
和为t的不同的组合方式的数目。
样例输入
5 5 1 2 3 4 5
样例输出
3
#include <bits/stdc++.h>
using namespace std;
int a[21];
long long dp[1001];
int n, t;
int main()
{
scanf("%d%d", &n, &t);
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
dp[0] = 1;
for(int i = 1; i <= n; i++)
{
for(int j = t; j >= a[i]; j--)
{
dp[j] = dp[j] + dp[j - a[i]];
}
}
printf("%d", dp[t]);
return 0;
}
问题 J: 【一本通基础DP背包】宠物小精灵之收服
[题目描述]
宠物小精灵是一部讲述小智和他的搭档皮卡丘一起冒险的故事。
一天,小智和皮卡丘来到了小精灵狩猎场,里面有很多珍贵的野生宠物小精灵。小智也想收服其中的一些小精灵。然而,野生的小精灵并不那么容易被收服。对于每一个野生小精灵而言,小智可能需要使用很多个精灵球才能收服它,而在收服过程中,野生小精灵也会对皮卡丘造成一定的伤害(从而减少皮卡丘的体力)。当皮卡丘的体力小于等于0时,小智就必须结束狩猎(因为他需要给皮卡丘疗伤),而使得皮卡丘体力小于等于0的野生小精灵也不会被小智收服。当小智的精灵球用完时,狩猎也宣告结束。
我们假设小智遇到野生小精灵时有两个选择:收服它,或者离开它。如果小智选择了收服,那么一定会扔出能够收服该小精灵的精灵球,而皮卡丘也一定会受到相应的伤害;如果选择离开它,那么小智不会损失精灵球,皮卡丘也不会损失体力。
小智的目标有两个:主要目标是收服尽可能多的野生小精灵;如果可以收服的小精灵数量一样,小智希望皮卡丘受到的伤害越小(剩余体力越大),因为他们还要继续冒险。
现在已知小智的精灵球数量和皮卡丘的初始体力,已知每一个小精灵需要的用于收服的精灵球数目和它在被收服过程中会对皮卡丘造成的伤害数目。请问,小智该如何选择收服哪些小精灵以达到他的目标呢?
输入
输入数据的第一行包含三个整数:N(0<N<1000),M(0<M<500),K(0<K<100),分别代表小智的精灵球数量、皮卡丘初始的体力值、野生小精灵的数量。
之后的K行,每一行代表一个野生小精灵,包括两个整数:收服该小精灵需要的精灵球的数量,以及收服过程中对皮卡丘造成的伤害。
输出
输出为一行,包含两个整数:C,R,分别表示最多收服C个小精灵,以及收服C个小精灵时皮卡丘的剩余体力值最多为R。
样例输入
10 100 5 7 10 2 40 2 50 1 20 4 20
样例输出
3 30
#include <bits/stdc++.h>
using namespace std;
long long dp[1000][1000];
int n, m, k;
//n代表小智的精灵球数量, m代表皮卡丘初始的体力值
//k代表野生小精灵的数量
int c[1000];
int w[1000];
//c[i]表示最多收服C个小精灵
//w[i]表示以及收服C个小精灵时皮卡丘的剩余体力值最多为w
int main()
{
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= k; i++)
{
scanf("%d%d", &c[i], &w[i]);
}
for(int i = 1; i <= k; i++)
{
for(int j = n; j >= c[i]; j--)
{
for(int l = m; l >= w[i]; l--)
{
dp[j][l] = max(dp[j][l], dp[j - c[i]][l - w[i]] + 1);
}
}
}
printf("%d ", dp[n][m]);
for(int i = 1; i <= m; i++)
{
if(dp[n][i] == dp[n][m])
{
printf("%d", m - i);
break;
}
}
return 0;
}
问题 K: 【一本通基础DP背包】买书
[题目描述]
小明手里有n元钱全部用来买书,书的价格为10元,20元,50元,100元。
问小明有多少种买书方案?(每种书可购买多本)
输入
一个整数 n,代表总共钱数。(0 ≤ n ≤ 1000)
输出
一个整数,代表选择方案种数。
样例输入
20
样例输出
2
#include <bits/stdc++.h>
using namespace std;
int a[5] = {0, 10, 20, 50, 100};
int n;
long long dp[1001];
int main()
{
scanf("%d", &n);
dp[0] = 1;
for(int i = 1; i <= 4; i++)
{
for(int j = a[i]; j <= n; j++)
{
dp[j] = dp[j] + dp[j - a[i]];
}
}
printf("%d", dp[n]);
return 0;
}
问题 L: 【一本通基础DP背包】Charm Bracelet
[题目描述]
经典0—1背包问题,有n个物品,编号为i的物品的重量为w[i],价值为c[i],现在要从这些物品中选一些物品装到一个容量为m的背包中,使得背包内物体在总重量不超过m的前提下价值尽量大。
输入
第1行:两个整数,n(物品数量,n≤3500)和m(背包容量,m≤12880)。
第2..n+1行::每行二个整数w[i],c[i],表示每个物品的重量和价值。
输出
仅一行,一个数,表示最大总价值。
样例输入
4 6 1 4 2 6 3 12 2 7
样例输出
23
#include <bits/stdc++.h>
using namespace std;
int w[3501], c[3501];
int dp[12881];
int m, n;
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
cin >> w[i] >> c[i];
}
for(int i = 1; i <= n; i++)
{
for(int j = m; j >= 1; j--)
{
if(j >= w[i])
{
dp[j] = max(dp[j - 1], dp[j - w[i]] + c[i]);
}
}
}
cout << dp[m];
return 0;
}
问题 M: 【一本通基础DP背包】NOIP 2001装箱问题
[题目描述]
有一个箱子容量为V(正整数,0≤V≤20000),同时有n个物品(0<n≤30),每个物品有一个体积(正整数)。
要求从n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
输入
第一行一个整数V
第二行一个整数n
第3行---第n+2行
为n个物品体积
输出
剩余空间为最小
样例输入
24 6 8 3 12 7 9 7
样例输出
0
#include <bits/stdc++.h>
using namespace std;
int v, n;
//v为背包容量,n为物品个数
int w[35];
//w[i]为物品重量
char a[100000];
int main()
{
cin >> v >> n;
for(int i = 0; i < 100000; i++)
{
a[i] = '0';
}
for(int i = 1; i <= n; i++)
{
cin >> w[i];
}
a[0] = '1';
for(int i = 1; i <= n; i++)
for(int j = v; j >= w[i]; j--)
if(a[j - w[i]] == '1')
a[j] = '1';
for(int i = v; i >= 0; i--)
if(a[i] == '1')
{
cout << v - i;
break;
}
// for(int i = 1; i <= n; i++)
// {
// cout << a[i] << " ";
// }
return 0;
}
问题 N: 【一本通基础DP背包】开餐馆
[题目描述]
信息学院的同学小明毕业之后打算创业开餐馆.现在共有n个地点可供选择。小明打算从中选择合适的位置开设一些餐馆。这 n个地点排列在同一条直线上。我们用一个整数序列m_1,m_2,...m_n来表示他们的相对位置。由于地段关系,开餐馆的利润会有所不同。我们用p_i表示在m_i处开餐馆的利润。为了避免自己的餐馆的内部竞争,餐馆之间的距离必须大于k。请你帮助小明选择一个总利润最大的方案。
输入
输入第一行是整数 T(1≤T≤1000),表明有T组测试数据。紧接着有T组连续的测试。每组测试数据有3行。
第1行:地点总数n(n<100), 距离限制k(k>0 且 k<1000);
第2行:n 个地点的位置m_1 , m_2, ... m_n(1000000>m_i>0 且为整数,升序排列);
第3行:n 个地点的餐馆利润p_1,p_2,...p_n(1000>p_i>0 且为整数)。
输出
对于每组测试数据可能的最大利润。
样例输入
2 3 11 1 2 15 10 2 30 3 16 1 2 15 10 2 30
样例输出
40 30
#include <bits/stdc++.h>
using namespace std;
int t;
int dp[100];
int m[100];
int p[100];
int n, k;
int main()
{
scanf("%d", &t);
for(int i = 1; i <= t; i++)
{
scanf("%d%d", &n, &k);
//地点总数n, 餐馆之间的距离必须大于k
for(int i = 1; i <= n; i++)
{
scanf("%d", &m[i]);
}
for(int i = 1; i <= n; i++)
{
scanf("%d", &p[i]);
}
for(int i = 1; i <= n; i++)
{
dp[i] = p[i];
}
//
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= i - 2; j++)
{
if(m[i] - m[j] > k)
{
dp[i] = max(dp[i], dp[i] + p[j]);
}
}
}
int max1 = 0;
for(int i = 1; i <= n; i++)
{
if(dp[i] > max1)
{
max1 = dp[i];
}
}
// for(int i = 1; i <= n; i++)
// {
// cout << dp[i] << " ";
// }
printf("%d\n", max1);
}
return 0;
}