第1题:公共子序列
我们称序列Z = < z1, z2, …, zk >是序列X = < x1, x2, …, xm >的子序列当且仅当存在 严格上升 的序列< i1, i2, …, ik >,使得对j = 1, 2, … ,k, 有xij = zj。比如Z = < a, b, f, c > 是X = < a, b, c, f, b, c >的子序列。 现在给出两个序列X和Y,你的任务是找到X和Y的最大公共子序列,也就是说要找到一个最长的序列Z,使得Z既是X的子序列也是Y的子序列。
时间限制:3000
内存限制:65536
输入
输入包括多组测试数据。每组数据包括一行,给出两个长度不超过200的字符串,表示两个序列。两个字符串之间由若干个空格隔开。
输出
对每组输入数据,输出一行,给出两个序列的最大公共子序列的长度。
样例输入
abcfbc abfcab
programming contest
abcd mnp
样例输出
4
2
0
要解决最大公共子序列问题,可以使用动态规划的方法。
以下是使用C语言实现的代码:
#include <stdio.h>
#include <string.h>
#define MAX_LENGTH 200
int max(int a, int b) {
return (a > b) ? a : b;
}
int longestCommonSubsequence(char X[], char Y[], int m, int n) {
int dp[MAX_LENGTH + 1][MAX_LENGTH + 1];
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
if (i == 0 || j == 0) {
dp[i][j] = 0;
} else if (X[i - 1] == Y[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
int main() {
char X[MAX_LENGTH + 1];
char Y[MAX_LENGTH + 1];
while (scanf("%s %s", X, Y) != EOF) {
int m = strlen(X);
int n = strlen(Y);
int length = longestCommonSubsequence(X, Y, m, n);
printf("%d\n", length);
}
return 0;
}
该代码使用一个二维数组dp来保存状态,其中dp[i][j]表示序列X的前i个字符和序列Y的前j个字符的最大公共子序列的长度。
首先,将dp[i][0]和dp[0][j]都设置为0,表示当一个序列的长度为0时,最大公共子序列的长度为0。
然后,从1到m和1到n的循环遍历,如果X[i-1]等于Y[j-1],则说明X的第i个字符和Y的第j个字符相同,将dp[i][j]设置为dp[i-1][j-1]的值加1,表示当前字符可以加入最大公共子序列。
如果X[i-1]不等于Y[j-1],则说明X的第i个字符和Y的第j个字符不相同,需要在X的前i-1个字符和Y的前j个字符的最大公共子序列和X的前i个字符和Y的前j-1个字符的最大公共子序列之间取最大值,即dp[i-1][j]和dp[i][j-1]的最大值。
最后,dp[m][n]即为X和Y的最大公共子序列的长度。
第2题:硬币问题
有N(不大于100)种硬币,编号为1至N,已知每种硬币的重量(不超过100的正整数)和面额(不超过100的正整数),每种硬币数量不限。选取总重量不超过C(不大于1000的正整数)的硬币,最多能获得多少总面额?
时间限制:1000
内存限制:65536
输入
第一行输入N 第二行输入C 第三行输入各硬币重量,用空格隔开 第四行输入各硬币价值,用空格隔开
输出
最大总面额
样例输入
3
5
1 2 5
1 3 6
样例输出
7
要解决硬币问题,可以使用动态规划的方法。
以下是使用C语言实现的代码:
#include <stdio.h>
#include <string.h>
#define MAX_COINS 100
#define MAX_WEIGHT 1000
int max(int a, int b) {
return (a > b) ? a : b;
}
int maxTotalValue(int coins[], int values[], int n, int capacity) {
int dp[MAX_WEIGHT + 1];
memset(dp, 0, sizeof(dp));
for (int i = 0; i < n; i++) {
for (int j = coins[i]; j <= capacity; j++) {
dp[j] = max(dp[j], dp[j - coins[i]] + values[i]);
}
}
return dp[capacity];
}
int main() {
int n;
scanf("%d", &n);
int capacity;
scanf("%d", &capacity);
int coins[MAX_COINS];
int values[MAX_COINS];
for (int i = 0; i < n; i++) {
scanf("%d", &coins[i]);
}
for (int i = 0; i < n; i++) {
scanf("%d", &values[i]);
}
int maxTotal = maxTotalValue(coins, values, n, capacity);
printf("%d\n", maxTotal);
return 0;
}
该代码使用一个一维数组dp来保存状态,其中dp[j]表示总重量为j时的最大总面额。
首先,将dp数组初始化为0。
然后,从第一种硬币到第N种硬币的循环遍历,对于每种硬币,从其重量coins[i]开始,到总重量C为止的范围内,更新dp[j]的值。更新方式为取dp[j]和dp[j - coins[i]] + values[i]的最大值,表示在总重量为j时,可以选择不放入当前硬币(即dp[j]的值)或放入当前硬币(总重量减去当前硬币重量的最大总面额,再加上当前硬币的面额)。
最后,dp[capacity]即为总重量不超过C时的最大总面额。
第3题:田忌赛马
你一定听过田忌赛马的故事吧?
如果3匹马变成1000匹,齐王仍然让他的马按从优到劣的顺序出赛,田忌可以按任意顺序选择他的赛马出赛。赢一局,田忌可以得到200两银子,输一局,田忌就要输掉200两银子,平局的话不输不赢。
请问田忌最多能赢多少银子?
时间限制:5000
内存限制:65536
输入
输入包含多组测试数据. 每组测试数据的第一行是一个整数n(1<=n<=1000),表示田忌和齐王都拥有n匹马。接下来一行是n个整数,表示田忌的马的速度,下一行也是n个整数,表示齐王的马的速度。 输入的最后以一个0表示结束。
输出
对每组数据,输出一个整数,表示田忌至多可以赢多少银子,如果田忌赢不了,就输出一个负数,表示田忌最少要输多少银子。
样例输入
3
92 83 71
95 87 74
2
20 20
20 20
2
20 19
22 18
0
样例输出
200
0
0
要解决田忌赛马问题,可以使用贪心算法的思想。
以下是使用C语言实现的代码:
#include <stdio.h>
#include <stdlib.h>
int compare(const void *a, const void *b) {
return *(int *)b - *(int *)a;
}
int calculateMaximumSilver(int tianjiHorses[], int qiwangHorses[], int n) {
int maxSilver = 0;
qsort(tianjiHorses, n, sizeof(int), compare);
qsort(qiwangHorses, n, sizeof(int), compare);
int tianjiIndex = 0;
int qiwangIndex = 0;
while (tianjiIndex < n && qiwangIndex < n) {
if (tianjiHorses[tianjiIndex] > qiwangHorses[qiwangIndex]) {
maxSilver += 200;
tianjiIndex++;
qiwangIndex++;
} else if (tianjiHorses[tianjiIndex] < qiwangHorses[qiwangIndex]) {
tianjiIndex++;
} else {
qiwangIndex++;
}
}
if (qiwangIndex == n) {
return maxSilver - 200 * (n - tianjiIndex);
} else {
return -maxSilver;
}
}
int main() {
int n;
while (scanf("%d", &n) == 1 && n != 0) {
int tianjiHorses[1000];
int qiwangHorses[1000];
for (int i = 0; i < n; i++) {
scanf("%d", &tianjiHorses[i]);
}
for (int i = 0; i < n; i++) {
scanf("%d", &qiwangHorses[i]);
}
int maxSilver = calculateMaximumSilver(tianjiHorses, qiwangHorses, n);
printf("%d\n", maxSilver);
}
return 0;
}
该代码首先使用qsort
函数对田忌和齐王的马的速度进行降序排序。
然后,使用两个指针tianjiIndex
和qiwangIndex
来遍历田忌和齐王的马的速度数组。
在每一轮比赛中,如果田忌的马速度大于齐王的马速度,则田忌赢得这局比赛,将最大银子数增加200,并将两个指针都向后移动一位。
如果田忌的马速度小于齐王的马速度,则田忌输掉这局比赛,将田忌的指针向后移动一位。
如果田忌的马速度等于齐王的马速度,则平局,将两个指针都向后移动一位。
最后,如果田忌的指针tianjiIndex
达到了数组长度n,说明田忌赢得了所有比赛,返回最大银子数;如果齐王的指针qiwangIndex
达到了数组长度n,说明田忌输掉了所有比赛,返回负的最大银子数。
第4题:上机
又到周末了,同学们陆陆续续开开心心的来到机房上机。jbr也不例外,但是他到的有点晚,发现有些机位上已经有同学正在做题,有些机位还空着。细心的jbr发现,一位同学来到机房,坐在机位i上,如果他的左右两边都空着,他将获得能力值a[i];如果当他坐下时,左边或者右边已经有一个人在上机了,他将获得能力值b[i];如果当他坐下时,他的左边右边都有人在上机,他将获得能力值c[i]。
同时他发现,已经在上机的同学不会受到刚要坐下的同学的影响,即他们的能力值只会在坐下时产生,以后不会发生变化;第一个机位左边没有机位,最后一个机位右边没有机位,无论何时坐在这两个机位上将无法获得c值。
这时jbr发现有一排机器还空着,一共有N个机位,编号1到N。这时有N位同学们陆陆续续来到机房,一个一个按照顺序坐在这排机位上。聪明的jbr想知道怎么安排座位的顺序,可以使这N位同学获得能力值的和最大呢?
时间限制:1000
内存限制:65536
输入
第一行一个整数N(1<= N <= 10000)
第二行N个数,表示a[i]
第三行N个数,表示b[i]
第四行N个数,表示c[i]
(1<= a[i],b[i],c[i] <=10000)
输出
一个整数,表示获得最大的能力值和
样例输入
4
1 2 2 4
4 3 3 1
2 1 1 2
样例输出
14
提示
第一位同学坐在第四个机位上,获得能力值4; 第二位同学坐在第三个机位上,获得能力值3; 第三位同学坐在第二个机位上,获得能力值3; 第四位同学坐在第一个机位上,获得能力值4; 总和为14。
要解决座位安排的问题,可以使用动态规划的方法。
以下是使用C语言实现的代码:
#include <stdio.h>
#include <stdlib.h>
int max(int a, int b) {
return (a > b) ? a : b;
}
int calculateMaxAbility(int a[], int b[], int c[], int n) {
int dp[3][10001] = {0};
dp[0][1] = a[1];
dp[1][1] = b[1];
dp[2][1] = 0;
for (int i = 2; i <= n; i++) {
dp[0][i] = max(dp[1][i - 1], dp[2][i - 1]) + a[i];
dp[1][i] = max(dp[0][i - 1], dp[2][i - 1]) + b[i];
dp[2][i] = max(dp[0][i - 1], dp[1][i - 1]) + c[i];
}
return max(max(dp[0][n], dp[1][n]), dp[2][n]);
}
int main() {
int n;
scanf("%d", &n);
int a[10001];
int b[10001];
int c[10001];
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= n; i++) {
scanf("%d", &b[i]);
}
for (int i = 1; i <= n; i++) {
scanf("%d", &c[i]);
}
int maxAbility = calculateMaxAbility(a, b, c, n);
printf("%d\n", maxAbility);
return 0;
}
该代码使用一个二维数组dp
来记录每个位置上的能力值。dp[i][j]
表示第j个机位上的同学选择第i种能力值时的最大能力值和。
初始化时,将第一个机位上的能力值分别赋值给dp[0][1]
、dp[1][1]
和dp[2][1]
。
然后,从第二个机位开始,利用动态规划的思想,根据题目给出的能力值计算出每个位置上三种情况的最大能力值和。
最后,返回dp
数组中最后一列的最大值,即为获得最大的能力值和。