第一题 小车问题
题目描述:
甲、乙两人同时从 A 地出发要尽快同时赶到 B 地。出发时 A 地有一辆小车,可是这辆小车除了驾驶员外只能带一人。已知甲、乙两人的步行速度一样,且小于车的速度。问:怎样利用小车才能使两人尽快同时到达。
输入格式:
仅一行,三个实数,分别表示 AB 两地的距离 s,人的步行速度 a,车的速度 b。
输出格式:
两人同时到达 B 地需要的最短时间,保留 66 位小数。
输入输出样例:
输入 | 输出 |
120 5 25 | 9.600000 |
说明/提示:
数据规模与约定
对于 100%100% 的数据,保证 0≤s,a,b≤10e9
思路:
本题的难点在于找出题目中暗含的等量关系,找出等量关系后,题目就变成了简单的解方程问题。乘车问题一般从路程和时间两个角度考虑等量关系。甲、乙两人步行速度一样且同时赶到,因此甲、乙乘车和步行的时间与路程都应该相等,可以将整段路程s分成三个部分,分别是两段步行的x和一段乘车的s-2x。而在时间上,b步行x的时间与a乘车后车返回接a所用的时间相等,可得等式x/a=(s-x+s-2x)/b,解出x后,带入x的值求出时间即可。
代码:
#include<stdio.h>
int main() {
double s, a, b;
double x, t;
scanf("%lf%lf%lf", &s, &a, &b);
x = 2*s*a / (b + 3 * a);
t = x / a + (s - x) / b;
printf("%.6lf", t);
}
第二题 笨小猴
题目描述:
笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼。但是他找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大!
这种方法的具体描述如下:假设 maxn 是单词中出现次数最多的字母的出现次数,minn 是单词中出现次数最少的字母的出现次数,如果 maxn−minn 是一个质数,那么笨小猴就认为这是个 Lucky Word,这样的单词很可能就是正确的答案。
输入格式:
一个单词,其中只可能出现小写字母,并且长度小于 100。
输出格式:
共两行,第一行是一个字符串,假设输入的的单词是 Lucky Word,那么输出 Lucky Word
,否则输出 No Answer
;
第二行是一个整数,如果输入单词是 Lucky Word
,输出 maxn−minnmaxn−minn 的值,否则输出 00。
输入输出样例:
输入 | 输出 |
error | Lucky Word 2 |
olympic | No Answer 0 |
说明/提示:
【输入输出样例 1 解释】
单词 error
中出现最多的字母 r 出现了 3 次,出现次数最少的字母出现了 1 次,3−1=2,2 是质数。
【输入输出样例 2 解释】
单词 olympic
中出现最多的字母 i 出现了 1 次,出现次数最少的字母出现了 1 次,1−1=0,0 不是质数。
思路:
对字符串进行两次遍历,一次比较字符串中的每一个字符,并声明两个变量依次存放字符存在次数的最大值和最小值,再用一个变量存放最值之间的差值,并判断差值是否为质数即可求解。
代码:
#include<stdio.h>
int main() {
int maxn = -1, minn = 101, cnt = 0;
int s, flag = 1;
char word[100];
scanf("%s", word);
for (int i = 0; word[i]; i++) {
cnt = -1;
for (int j = 0; word[j]; j++) {
if (word[j] == word[i]) {
cnt++;
}
}
if (cnt < minn) {
minn = cnt;
}
if (cnt > maxn) {
maxn = cnt;
}
} //遍历字符串得到最大值和最小值
s = maxn - minn;
if (s == 1 || s==0) {
flag = 0;
}
for (int i = 2; i < s; i++) {
if (s % i == 0) {
flag = 0;
break;
}
} //判断是否为质数
if (flag == 1) {
printf("Lucky Word\n%d", s);
} else {
printf("No Answer\n0");
}
}
第三题 拯救oibh总部
题目描述:
oibh 被突来的洪水淹没了,还好 oibh 总部有在某些重要的地方起一些围墙。用 *
号表示,而一个四面被围墙围住的区域洪水是进不去的。
oibh 总部内部也有许多重要区域,每个重要区域在图中用一个 0
表示。
现在给出 oibh 的围墙建设图,问有多少个没被洪水淹到的重要区域。
输入格式:
第一行为两个正整数 x,y。
接下来 x 行,每行 y 个整数,由 *
和 0
组成,表示 oibh 总部的建设图。
输出格式:
输出没被水淹没的 oibh 总部的 0
的数量。
输入输出样例:
输入 | 输出 |
4 5 00000 00*00 0*0*0 00*00 | 1 |
5 5 ***** *0*0* **0** *0*0* ***** | 5 |
说明/提示:
对于 100% 的数据,1≤x,y≤500。
思路:
这道题如果直接遍历二维数组,无法判断哪些位置的0被“*”围起来了,除非所有“0”都在“*”围成的围墙中,因此考虑使用深度优先搜索。在输入数据后,分别从第一行和最后一行以及第一列和最后一列进行深度优先搜索,并将搜索到的“0”标记为“*”,“*”即为不可通行的区域。在搜索完成后,再对数组进行遍历,统计数组中存在多少个"0"即可。
代码:
#include<stdio.h>
#include<stdlib.h>
int n, m;
char map[500][500];
int location[4][2] = { {-1, 0}, {1, 0}, {0, 1}, {0, -1} };
int Check(int x, int y) {
if (x >= 0 && x < n && y >= 0 && y < m) {
return 1;
}
return 0;
} //判断当前位置是否到达边界
void DFS(int x, int y) {
int tx, ty;
if (Check(x, y) == 0) {
return 0;
}
if (map[x][y] == '*') {
return 0;
} //结束边界判断
map[x][y] = '*';
for (int i = 0; i < 4; i++) {
tx = x + location[i][0];
ty = y + location[i][1];
DFS(tx, ty);
} //继续向下一个位置搜索
return 0;
} //深度搜索函数
int main() {
int sum = 0;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) {
scanf("%s", map[i]);
}
if (n > 1) {
for (int j = 0; j < m; j++) {
DFS(0, j);
DFS(n-1, j);
} //如果只有一行,则只需搜索这一行
} else {
for (int j = 0;j < m; j++) {
DFS(0, j);
}
} //对第一行和最后一行进行深度优先搜索
for (int i = 1; i < n - 1; i++) {
DFS(i, 0);
DFS(i, m-1);
} //对一列和最后一列进行深度优先搜索
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (map[i][j] == '0') {
sum++;
}
}
} //遍历统计“0”的个数
printf("%d", sum);
return 0;
}
第四题 队列安排
题目描述:
一个学校里老师要将班上 N 个同学排成一列,同学被编号为 1∼N,他采取如下的方法:
-
先将 1 号同学安排进队列,这时队列中只有他一个人;
-
2∼N 号同学依次入列,编号为 i 的同学入列方式为:老师指定编号为 i 的同学站在编号为 1∼(i−1) 中某位同学(即之前已经入列的同学)的左边或右边;
-
从队列中去掉 M 个同学,其他同学位置顺序不变。
在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。
输入格式:
第一行一个整数 N,表示了有 N 个同学。
第 2∼N 行,第 i 行包含两个整数 k,p,其中 k 为小于 i 的正整数,p 为 0 或者 1。若p 为 0,则表示将 i 号同学插入到 k 号同学的左边,p 为 1 则表示插入到右边。
第 N+1 行为一个整数 M,表示去掉的同学数目。
接下来 M 行,每行一个正整数 x,表示将 x 号同学从队列中移去,如果 x 号同学已经不在队列中则忽略这一条指令。
输出格式:
一行,包含最多 N 个空格隔开的整数,表示了队列从左到右所有同学的编号。
输入输出样例:
输入 | 输出 |
4 1 0 2 1 1 0 2 3 3 | 2 4 1 |
说明/提示:
【样例解释】
将同学 2 插入至同学 1 左边,此时队列为:
2 1
将同学 3 插入至同学 2 右边,此时队列为:
2 3 1
将同学 4 插入至同学 1 左边,此时队列为:
2 3 4 1
将同学 3 从队列中移出,此时队列为:
2 4 1
同学 3 已经不在队列中,忽略最后一条指令
最终队列:
2 4 1
【数据范围】
对于 20% 的数据,1≤N≤10。
对于 40% 的数据,1≤N≤1000。
对于 100% 的数据,1<M≤N≤105。
思路:
这道题要对一个队列实现插入、删除,并且要插入在元素的前后任意位置,在插入和删除的过程中其实隐含了大量对元素的查找操作,可以考虑使用数组来模拟双向链表实现题目要求的操作。
代码:
#include<stdio.h>
#include<stdlib.h>
int pre[100005];
int next[100005];
int nums[100005] = { 0 };
int main() {
int n;
scanf("%d", &n);
next[0] = 1; //"1"为第一个数
pre[0] = n; //“n”为最后一个数
next[1] = 0; //“1”之后暂时没有任何数
pre[1] = 0; //“1”为第一个数
int k, p;
for (int i = 2; i <= n; i++) {
scanf("%d%d", &k, &p);
if (p == 0) {
next[i] = k;
pre[i] = pre[k];
next[pre[i]] = i;
pre[k] = i;
} else {
next[i] = next[k];
pre[next[k]] = i;
next[k] = i;
pre[i] = k;
}
} //插入元素
int m, x;
scanf("%d", &m);
while (m--) {
scanf("%d", &x);
if (nums[x] == 0) {
next[pre[x]] = next[x];
pre[next[x]] = pre[x];
nums[x] = 1;
}
} //删除元素
int i = next[0];
while (i != 0) {
printf("%d ", i);
i = next[i];
} //打印元素
}
第五题 句子反转
题目描述:
给定一行句子,每个词之间用空格隔开,要么是全小写英文单词,要么是全大写英文单词,要么是自然数。
要求将这些单词倒序输出。而且对于每个单词,如果是小写词,应当转为大写;如果是大写词,应当转为小写;如果是自然数,应该倒转输出。
输入格式:
仅一行,即需要反转的句子。
输出格式:
仅一行,表示程序对句子的处理结果。
输入输出样例:
输入 | 输出 |
we choose TO go 2 the 123 moon | MOON 321 THE 2 GO to CHOOSE WE |
说明/提示:
样例解释
首先应当按单词逆序,即:
moon 123 the 2 go TO choose we
小写变大写、大写变小写、倒转自然数之后,得到最终结果:
MOON 321 THE 2 GO to CHOOSE WE
数据规模与约定
对于 100% 的数据,句子中包含的单词数量不超过 1000,每个单词长度不超过 6。
思路:
该题要求将字符串倒转并转换大小写,但这里的倒转并不意味着将字符倒转输出,而是将单词的顺序倒转输出,因此,可以用两个整型变量模拟字符指针,来实现题目所要求的操作。
代码:
#include<stdio.h>
#include<string.h>
int main() {
int m, n;
char ch[6050];
fgets(ch,6050,stdin);
m = strlen(ch) - 1;
n = strlen(ch) - 1; //让m、n指向字符串末尾
while (m > 0) {
while (m > 0 && ch[m] != ' ') {
m--; //让m指向每一个单词前的空格处
}
if (ch[m + 1] <= 'Z' && ch[m + 1] >= 'A' || ch[m + 1] <= 'z' && ch[m + 1] >= 'a') {
if (m > 0) {
if (ch[m + 1] <= 'Z' && ch[m + 1] >= 'A') {
for (int i = m + 1; i <= n; i++) {
ch[i] = ch[i] + 'a' - 'A';
printf("%c", ch[i]);
}
} else {
for (int i = m + 1; i <= n; i++) {
ch[i] = ch[i] + 'A' - 'a';
printf("%c", ch[i]);
}
}
} else {
if (ch[m] <= 'Z' && ch[m] >= 'A') {
for (int i = m; i <= n; i++) {
ch[i] = ch[i] + 'a' - 'A';
printf("%c", ch[i]);
}
} else {
for (int i = m; i <= n; i++) {
ch[i] = ch[i] + 'A' - 'a';
printf("%c", ch[i]);
}
}
} //如果字符为字母,利用ASCII码实现大小写转换,并以单词为单位按顺序输出
} else {
if (m > 0) {
for (int i = n; i > m; i--) {
printf("%c", ch[i]);
}
} else {
for (int i = n; i >= m; i--) {
printf("%c", ch[i]);
}
}
} //如果字符为数字,逆序输出
if (m > 0) {
printf(" ");
} //在单词间输出空格
m--;
n = m;
} //让m、n指向前一个单词的末尾
return 0;
}
第六题 明明的随机数
题目描述:
明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了 N 个 1 到 1000 之间的随机整数 (N≤100),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作。
输入格式:
输入有两行,第 1 行为 1个正整数,表示所生成的随机数的个数 N。
第 2 行有 N 个用空格隔开的正整数,为所产生的随机数。
输出格式:
输出也是两行,第 1 行为 1 个正整数 M,表示不相同的随机数的个数。
第 2 行为 M 个用空格隔开的正整数,为从小到大排好序的不相同的随机数。
输入输出样例:
输入 | 输出 |
10 20 40 32 67 40 20 89 300 400 15 | 8 15 20 32 40 67 89 300 400 |
说明/提示:
无
思路:
这道题我采用的是桶的方法,申请三个数组arr,brr,crr,对第一个数组遍历,假设存在数字a,则brr[a]=1,并让统计变量自增。这样的话,重复的元素也只会记录一次。然后再对作为桶的数组brr遍历,如果brr[a]=1,则将a赋值到crr数组中。最后直接输出crr数组中的数据,得到的就是去重且排序完后的数据。
代码
#include<stdio.h>
int main() {
int n, k = 0, cnt = 0;
int arr[100] = { 0 }, brr[1001] = { 0 }, crr[100];
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
for (int i = 0; i < n; i++) {
if (arr[i] != 0) {
int t = arr[i];
if (brr[t] == 0) {
cnt++;
}
brr[t] = 1;
}
} //将数据记录进“桶”中
for (int j = 0; j < 1001; j++) {
if (brr[j] == 1) {
crr[k] = j;
k++;
}
} //将桶中的数据赋到新数组中
printf("%d\n", cnt);
for (int i = 0; i < cnt; i++) {
if (i != 0) {
printf(" ");
}
printf("%d", crr[i]);
} //打印新数组
}
第七题 小A的糖果
题目描述:
小 A 有 n 个糖果盒,第 i 个盒中有 ai 颗糖果。
小 A 每次可以从其中一盒糖果中吃掉一颗,他想知道,要让任意两个相邻的盒子中糖的个数之和都不大于 x,至少得吃掉几颗糖。
输入格式:
输入的第一行是两个用空格隔开的整数,代表糖果盒的个数 n 和给定的参数 x。
第二行有 n 个用空格隔开的整数,第 i 个整数代表第 i 盒糖的糖果个数 ai。
输出格式:
输出一行一个整数,代表最少要吃掉的糖果的数量。
输入输出样例:
输入 | 输出 |
3 3 2 2 2 | 1 |
6 1 1 6 1 2 0 4 | 11 |
5 9 3 1 4 1 5 | 0 |
说明/样例:
样例输入输出 1 解释
吃掉第 2 盒中的一个糖果即可。
样例输入输出 2 解释
第 2 盒糖吃掉 66 颗,第 4 盒吃掉 22 颗,第 6 盒吃掉 33 颗。
数据规模与约定
- 对于 30% 的数据,保证 n≤20,ai,x≤100。
- 对于 70% 的数据,保证 n≤10e3,ai,x≤105。
- 对于 100% 的数据,保证 2≤n≤10e5,0≤ai,x≤109。
思路:
该题可采用贪心算法求解,一对元素中,改变第一个只会使第一对组合符合要求,但如果改变第二个,便可以使得相邻三个元素到符合要求,所以尽量改变每一对中的第二个元素。
代码:
#include<stdio.h>
int main() {
int n, x;
long long sum=0;
long long arr[100010];
scanf("%d%d", &n, &x);
for (int j = 1; j <= n; j++) {
scanf("%lld", &arr[j]);
if (arr[j] + arr[j - 1] > x) {
int k = arr[j];
arr[j] = x - arr[j - 1];
sum += k - arr[j];
}
}
printf("%lld", sum);
}