第1题:酒鬼
Santo刚刚与房东打赌赢得了一间在New Clondike 的大客厅。今天,他来到这个大客厅欣赏他的奖品。房东摆出了一行瓶子在酒吧上。瓶子里都装有不同体积的酒。令Santo高兴的是,瓶子中的酒都有不同的味道。房东说道:“你可以喝尽可能多的酒,但是一旦打开酒盖你就必须把它喝完,喝完一瓶后把它放回原处。还有一件最重要的事,你必须从左至右依次喝,并且不能连续超过三瓶,不然会给你带来坏运气。”现在可怜的Santo站在酒吧前努力的想着,他到底应该喝哪几瓶才能使喝的酒最多呢?请帮助他找出他应该喝的酒瓶号,因为思考让他感到不安。
时间限制:2000
内存限制:131072
输入
第一行一个整数N,有N个酒瓶。N<=700接下有N行,第I+1行的数字代表酒瓶I中酒的体积。
输出
一个数字,喝的酒的最大总体积。遵守以上规则,使得三个连续瓶子中至少一个瓶子是满的。
样例输入
6
6
10
13
9
8
1
样例输出
33
要解决这个问题,我们可以使用动态规划来找到喝的酒的最大总体积。
我们可以定义两个数组 dp
和 dpFull
,其中 dp[i]
表示从前 i
个酒瓶中喝的酒的最大总体积,而 dpFull[i]
表示从前 i
个酒瓶中喝的酒的最大总体积,并且前 i
个酒瓶中的最后一个瓶子是满的。
根据题目规则,我们可以得到递推关系:
(1)如果第 i
个瓶子不喝,则 dp[i] = dp[i-1]
。
(2)如果第 i
个瓶子喝,则前一个瓶子不能喝,即 dp[i] = dp[i-2] + volume[i]
。
(3)如果前两个瓶子都喝,则前三个瓶子不能连续喝,即 dp[i] = dpFull[i-3] + volume[i]
。
对于 dpFull
数组,有以下递推关系:
(1)如果第 i
个瓶子不喝,则 dpFull[i] = dpFull[i-1]
。
(2)如果第 i
个瓶子喝,则前一个瓶子不能喝,即 dpFull[i] = dp[i-2] + volume[i]
。
注意,初始情况下,当只有一个酒瓶时,即 dp[1] = volume[1]
和 dpFull[1] = volume[1]
。
下面是使用C语言编写的解题代码:
#include <stdio.h>int max(int a, int b) {return (a > b) ? a : b;
}int main() {int N;scanf("%d", &N);int volume[701];int dp[701] = {0};int dpFull[701] = {0};for (int i = 1; i <= N; i++) {scanf("%d", &volume[i]);}dp[1] = volume[1];dpFull[1] = volume[1];for (int i = 2; i <= N; i++) {dp[i] = max(dp[i - 1], max(dp[i - 2] + volume[i], dpFull[i - 3] + volume[i]));dpFull[i] = max(dpFull[i - 1], dp[i - 2] + volume[i]);}printf("%d\n", dp[N]);return 0;
}
在上述代码中,我们首先定义了一个辅助函数 max
来返回两个数中的较大值。
然后,我们定义了一个整数数组 volume
,用于存储每个酒瓶的体积。同时,我们定义了两个动态规划数组 dp
和 dpFull
,用于计算喝的酒的最大总体积。
接下来,我们使用一个循环来读取输入的酒瓶体积,并存储到 volume
数组中。
然后,我们根据递推关系和初始情况,使用两个循环来计算 dp
和 dpFull
数组的值。
最后,我们输出 dp[N]
,即喝的酒的最大总体积。
编译并运行上述代码可以解决该问题。你可以使用 C 语言编译器将代码保存为 .c
文件,然后编译并运行该文件来获取结果。
第2题:重启系统
小明帮助管理一个处理数据的计算系统,有N个待处理的任务,需要按照顺序来完成这些任务,即每次所完成任务的编号都要大于前一个完成任务的编号,且单个任务不可以分解完成。计算系统运行着一个奇怪的保护程序,它限制了系统当前所能处理的数据量不能超过上次完成任务所处理的数据量。重启系统可以使它立刻恢复到最高性能(一开始系统拥有最高性能,最高性能大于任何待处理任务的数据量)。小明有一次重启系统的权限(也可以不使用),你能帮他算出最多能完成几个任务吗?
时间限制:1000
内存限制:65536
输入
第一行: N (2 <= N <= 1000) 待处理的任务数 第二行: N个整数,每个任务的数据量
输出
输出只包括一行,这一行只包含一个整数,表示最多能完成的任务数。
样例输入
10
1 5 4 3 2 10 9 8 7 6
样例输出
9
提示
回想一下最长上升子序列问题
要解决这个问题,我们可以使用动态规划来找到最多能完成的任务数。
我们可以定义一个数组 dp
,其中 dp[i]
表示以第 i
个任务结尾的最长上升子序列的长度。
根据题目规则,我们可以得到递推关系:
如果第 i
个任务的数据量大于前面任意一个已完成任务的数据量,则 dp[i] = max(dp[j] + 1)
,其中 j
取值范围为 [0, i-1]
,且 volume[i] > volume[j]
。
注意,初始情况下,当只有一个任务时,即 dp[1] = 1
。
最后,我们遍历整个 dp
数组,找到最大的值,即为最多能完成的任务数。
下面是使用C语言编写的解题代码:
#include <stdio.h>int max(int a, int b) {return (a > b) ? a : b;
}int main() {int N;scanf("%d", &N);int volume[1001];int dp[1001] = {0};for (int i = 1; i <= N; i++) {scanf("%d", &volume[i]);}dp[1] = 1;for (int i = 2; i <= N; i++) {for (int j = 1; j <= i - 1; j++) {if (volume[i] > volume[j]) {dp[i] = max(dp[i], dp[j] + 1);}}}int maxTasks = 0;for (int i = 1; i <= N; i++) {maxTasks = max(maxTasks, dp[i]);}printf("%d\n", maxTasks);return 0;
}
在上述代码中,我们首先定义了一个辅助函数 max
来返回两个数中的较大值。
然后,我们定义了一个整数数组 volume
,用于存储每个任务的数据量。
接下来,我们使用一个循环来读取输入的任务数据量,并存储到 volume
数组中。
然后,我们使用两个嵌套循环来计算 dp
数组的值,根据递推关系更新最长上升子序列的长度。
最后,我们遍历整个 dp
数组,找到最大的值,即为最多能完成的任务数。
编译并运行上述代码可以解决该问题。你可以使用 C 语言编译器将代码保存为 .c
文件,然后编译并运行该文件来获取结果。
第3题:鸣人的影分身
在火影忍者的世界里,令敌人捉摸不透是非常关键的。我们的主角漩涡鸣人所拥有的一个招数——多重影分身之术——就是一个很好的例子。
影分身是由鸣人身体的查克拉能量制造的,使用的查克拉越多,制造出的影分身越强。
针对不同的作战情况,鸣人可以选择制造出各种强度的影分身,有的用来佯攻,有的用来发起致命一击。
那么问题来了,假设鸣人的查克拉能量为M,他影分身的个数为N,那么制造影分身时有多少种(用K表示)不同的分配方法?(影分身可以被分配到0点查克拉能量)
时间限制:1000
内存限制:65536
输入
第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1 <= M,N <= 10。
输出
对输入的每组数据M和N,用一行输出相应的K。
样例输入
1
7 3
样例输出
8
要解决这个问题,我们可以使用动态规划来计算不同的分配方法。
我们可以定义一个二维数组 dp
,其中 dp[i][j]
表示当查克拉能量为 i
时,制造 j
个影分身的不同分配方法数。
根据题目规则,我们可以得到递推关系:
(1)当 j = 0
时,即没有影分身需要制造,那么无论查克拉能量为多少,只有一种分配方法,即不分配任何查克拉给影分身,即 dp[i][0] = 1
。
(2)当 i = 0
时,即查克拉能量为0,无法制造任何影分身,那么无论需要制造多少个影分身,都没有任何一种分配方法,即 dp[0][j] = 0
。
(3)对于其他情况,我们可以将第 j
个影分身的查克拉能量从0到 i
进行遍历,并将其分配给第 j
个影分身,然后计算剩余的查克拉能量 i - k
分配给前 j-1
个影分身的不同分配方法数之和,即 dp[i][j] = dp[i][j] + dp[i - k][j - 1]
,其中 k
取值范围为 [0, i]
。
最后,我们遍历 dp[M][N]
,即查克拉能量为 M
,制造 N
个影分身的情况下的不同分配方法数。
下面是使用C语言编写的解题代码:
#include <stdio.h>int main() {int t;scanf("%d", &t);while (t--) {int M, N;scanf("%d %d", &M, &N);int dp[11][11] = {0};for (int j = 0; j <= N; j++) {dp[0][j] = 0;}for (int i = 0; i <= M; i++) {dp[i][0] = 1;}for (int i = 1; i <= M; i++) {for (int j = 1; j <= N; j++) {for (int k = 0; k <= i; k++) {dp[i][j] += dp[i - k][j - 1];}}}printf("%d\n", dp[M][N]);}return 0;
}
在上述代码中,我们首先读取测试数据的数目 t
。
然后,我们使用一个 while
循环来处理每组测试数据。
在每组测试数据中,我们首先读取 M
和 N
,即查克拉能量和影分身的个数。
接下来,我们定义一个二维数组 dp
,用于计算不同的分配方法数。
然后,我们根据递推关系对 dp
数组进行初始化。
接着,我们使用三个嵌套循环来计算 dp
数组的值,根据递推关系更新不同的分配方法数。
最后,我们输出 dp[M][N]
,即相应的不同分配方法数。
编译并运行上述代码可以解决该问题。你可以使用 C 语言编译器将代码保存为 .c
文件,然后编译并运行该文件来获取结果。
第4题:宠物小精灵之收服
宠物小精灵是一部讲述小智和他的搭档皮卡丘一起冒险的故事。
一天,小智和皮卡丘来到了小精灵狩猎场,里面有很多珍贵的野生宠物小精灵。小智也想收服其中的一些小精灵。然而,野生的小精灵并不那么容易被收服。对于每一个野生小精灵而言,小智可能需要使用很多个精灵球才能收服它,而在收服过程中,野生小精灵也会对皮卡丘造成一定的伤害(从而减少皮卡丘的体力)。当皮卡丘的体力小于等于0时,小智就必须结束狩猎(因为他需要给皮卡丘疗伤),而使得皮卡丘体力小于等于0的野生小精灵也不会被小智收服。当小智的精灵球用完时,狩猎也宣告结束。
我们假设小智遇到野生小精灵时有两个选择:收服它,或者离开它。如果小智选择了收服,那么一定会扔出能够收服该小精灵的精灵球,而皮卡丘也一定会受到相应的伤害;如果选择离开它,那么小智不会损失精灵球,皮卡丘也不会损失体力。
小智的目标有两个:主要目标是收服尽可能多的野生小精灵;如果可以收服的小精灵数量一样,小智希望皮卡丘受到的伤害越小(剩余体力越大),因为他们还要继续冒险。
现在已知小智的精灵球数量和皮卡丘的初始体力,已知每一个小精灵需要的用于收服的精灵球数目和它在被收服过程中会对皮卡丘造成的伤害数目。请问,小智该如何选择收服哪些小精灵以达到他的目标呢?
时间限制:1000
内存限制:65536
输入
输入数据的第一行包含三个整数:N(0 < N < 1000),M(0 < M < 500),K(0 < K < 100),分别代表小智的精灵球数量、皮卡丘初始的体力值、野生小精灵的数量。 之后的K行,每一行代表一个野生小精灵,包括两个整数:收服该小精灵需要的精灵球的数量,以及收服过程中对皮卡丘造成的伤害。
输出
输出为一行,包含两个整数:C,R,分别表示最多收服C个小精灵,以及收服C个小精灵时皮卡丘的剩余体力值最多为R。
样例输入
样例输入1:
10 100 5
7 10
2 40
2 50
1 20
4 20
样例输入2:
10 100 5
8 110
12 10
20 10
5 200
1 110
样例输出
样例输出1:
3 30
样例输出2:
0 100
提示
对于样例输入1:小智选择:(7,10) (2,40) (1,20) 这样小智一共收服了3个小精灵,皮卡丘受到了70点伤害,剩余100-70=30点体力。所以输出3 30 对于样例输入2:小智一个小精灵都没法收服,皮卡丘也不会收到任何伤害,所以输出0 100
运行测试
控制台:清理控制台
解题思路如下:
(1)首先,我们需要使用动态规划来解决这个问题。我们定义一个二维数组 dp[N+1][M+1]
,其中 dp[i][j]
表示使用前 i
个精灵球,对于剩余体力为 j
的情况下,可以收服的最多小精灵数量。
(2)初始化 dp
数组:对于 dp[0][j]
(即使用 0 个精灵球),无论剩余体力是多少,都无法收服小精灵,所以初始化为 0。对于 dp[i][0]
(即剩余体力为 0),无论使用多少个精灵球,都无法继续冒险,所以初始化为 0。
(3)根据递推关系更新 dp
数组的值:对于每一个小精灵,我们有两种选择:收服它或者离开它。如果选择收服它,我们需要考虑两个因素:使用精灵球的数量和对皮卡丘造成的伤害。我们遍历所有可能的精灵球数和伤害,更新 dp[i][j]
的值为 max(dp[i-1][j]
, dp[i-1][j-damage] + balls
),其中 balls
和 damage
分别表示第 i
个小精灵需要的精灵球数和对皮卡丘造成的伤害。
(4)最后,我们遍历整个 dp
数组,找到收服小精灵数量最大的值 max_captured
,以及对应的剩余体力最大值 max_remain_hp
。
(5)输出 max_captured
和 max_remain_hp
。
以下是根据题目要求的 C 语言解题代码:
#include <stdio.h>int max(int a, int b) {return (a > b) ? a : b;
}int main() {int N, M, K;scanf("%d %d %d", &N, &M, &K);int dp[N + 1][M + 1];int balls[K], damage[K];for (int i = 0; i < K; i++) {scanf("%d %d", &balls[i], &damage[i]);}for (int i = 0; i <= N; i++) {for (int j = 0; j <= M; j++) {if (i == 0 || j == 0) {dp[i][j] = 0;} else {dp[i][j] = dp[i - 1][j];if (j >= damage[i - 1]) {dp[i][j] = max(dp[i][j], dp[i - 1][j - damage[i - 1]] + balls[i - 1]);}}}}int max_captured = 0;int max_remain_hp = M;for (int i = 0; i <= N; i++) {for (int j = 0; j <= M; j++) {if (dp[i][j] > max_captured) {max_captured = dp[i][j];max_remain_hp = M - j;}}}printf("%d %d\n", max_captured, max_remain_hp);return 0;
}
这里我们使用了一个 max
函数来返回两个数中的较大值。
然后,我们定义了一个二维数组 dp
来存储中间结果,并且定义了两个数组 balls
和 damage
来存储每个野生小精灵的精灵球数和伤害值。
接下来,我们使用两个嵌套循环来计算 dp
数组的值,根据递推关系更新 dp[i][j]
的值。
最后,我们遍历整个 dp
数组,找出最大值以及对应的索引,即可以收服的最大数量的小精灵和皮卡丘剩余体力的最大值。然后输出这两个值。
运行以上代码,即可得到结果。你可以将代码保存为 .c
文件,然后编译运行该文件来获取结果。