说明:以下内容是nenuacm 2019 新生训练#2 的题解,代码实现是纯C语言,仅供新生参考
目录
A - 求奇数的乘积
题解:O(n)枚举n个数, 当遇到奇数则乘上。
#include <stdio.h>
int main()
{
int n, x;
while(scanf("%d", &n) != EOF){
long long ans = 1;
while(n --){
scanf("%d", &x);
if(x & 1) ans *= x;
}
printf("%lld\n", ans);
}
return 0;
}
B - 平方和与立方和
题解:ans1记录偶数的平方和, ans2记录奇数的立方和, 枚举从n到m的整数,是偶数则ans1加上其平方, 奇数则ans2加上其立方
#include <stdio.h>
int main()
{
int n, m, i;
while(scanf("%d %d", &n, &m) != EOF){
long long ans1 = 0, ans2 = 0;
if(n > m){///防止n > m
int tmp = n; n = m; m = tmp;
}
for(i = n; i <= m; i ++){
if(i & 1)ans2 += i * i * i;
else ans1 += i * i;
}
printf("%lld %lld\n", ans1, ans2);
}
return 0;
}
C - 绝对值排序
题解:要学会利用C语言里现有的函数库,调用头文件stdlib.h下的qsort函数, 另外写一个cmp比较函数,具体键代码实现
#include <stdio.h>
#include <stdlib.h>
int Abs(int x){
if(x < 0) x = -x;
return x;
}
int cmp(const void*a,const void*b){
return Abs(*(int*)b) - Abs(*(int*)a);///对绝对值从大到小排序
}
/**
cmp函数通用写法
int cmp(const void *a, const void *b){
return *(int*)a - *(int*)b; ///按从小到大排序
}
**/
int main()
{
int n, i;
int a[110];
while(scanf("%d", &n) != EOF){
if(n == 0) break;
for(i = 0; i < n; i ++) scanf("%d", &a[i]);
qsort(a, n, sizeof(int), cmp);
/**
qsort函数通用写法 qsort(start, n, size, cmp)
start->待排数组开始位置
n->表示从start往后n个元素
size->每个元素所占内存大小
cmp->比较函数,可定义排序方式
**/
for(i = 0; i < n - 1; i ++){
printf("%d ", a[i]);
}
printf("%d\n", a[n-1]);
}
return 0;
}
也可自己手动实现排序算法,以选择排序为例:只需修改标条件即可
#include <stdio.h>
int Abs(int x){
if(x < 0) x = -x;
return x;
}
void SelectSort(int a[], int n){
int i, j;
for(i = 1; i <= n - 1; i ++){
for(j = i + 1; j <= n; j ++){
if(Abs(a[i]) < Abs(a[j])){///表明按绝对值从大到小循序排序
int tmp = a[i]; a[i] = a[j]; a[j] = tmp;
}
}
}
}
int main()
{
int a[110], i;
int n;
while(scanf("%d", &n)){
if(n == 0) break;
for(i = 1; i <= n; i ++){
scanf("%d", &a[i]);
}
SelectSort(a, n);
for(i = 1; i < n; i ++){
printf("%d ", a[i]);
}
printf("%d\n", a[n]);
}
return 0;
}
D - 求数列的和
题解:按题意循环m次即可,对于一个数开方需调用头文件math.h下的sqrt()函数, 注意要用浮点型(double即可)数据类型, 保留两位小数输出方法printf("%.2f\n", ans);
#include <stdio.h>
#include <math.h>
int main()
{
double n;
int m;
while(scanf("%lf %d", &n, &m) != EOF){
double ans = 0;
while(m --){
ans += n;
n = sqrt(n);
}
printf("%.2f\n", ans);
}
return 0;
}
E - 三角形
题解:判断是否构成三角形,利用三角形的任意两边之和大于第三边,任意两边只差小于第三边的性质,
即判断a+b>c&&b+c>a&&c+a>b是否成立。
注意读入应该是浮点型,因为题目没有说边长一定是整形
#include <stdio.h>
int main()
{
int T; scanf("%d", &T);
while(T --){
double a, b, c; scanf("%lf %lf %lf", &a, &b, &c);
if(a + b > c && b + c > a && c + a > b){
printf("YES\n");
}
else printf("NO\n");
}
return 0;
}
F - 数值统计
题解:定义ans1记录负数个数, ans2记录零的个数, ans3记录整数的个数,注意输入用浮点型数据类型
#include <stdio.h>
#include <math.h>
#define eps 1e-6
int main()
{
int n;
while(scanf("%d", &n) != EOF){
if(n == 0) break;
int cnt1 = 0, cnt2 = 0, cnt3 = 0;
while(n --){
double x; scanf("%lf", &x);
if(fabs(x) < eps) cnt2 ++;///若x满足此条件,默认x为0
else if(x < 0) cnt1 ++;
else if(x > 0) cnt3 ++;
}
printf("%d %d %d\n", cnt1, cnt2, cnt3);
}
return 0;
}
G - 统计元音
题解:按题意遍历字符数组,当出现元音字母对应个数加一, 注意读入字符串不能用scanf函数,因为输入字符串中间可能有空行,使用gets(s)即可,此函数按行输入字符串
注意输出处理最后一个样例没有空行的小技巧
#include <stdio.h>
#include <string.h>
int main()
{
char s[110];
int a[5];
int Case = 0, i;
int T; scanf("%d", &T);
getchar(); ///注意getcha(),没有会很麻烦,不信试试
while(T --){
memset(a, 0, sizeof a);
if(Case) printf("\n");///保证最后一个样例没又多余空行输出
Case ++;
gets(s);///按行读入
int len = strlen(s);
for(i = 0; i < len; i ++){
if(s[i] == 'a') a[0] ++;
else if(s[i] == 'e') a[1] ++;
else if(s[i] == 'i') a[2] ++;
else if(s[i] == 'o') a[3] ++;
else if(s[i] == 'u') a[4] ++;
}
printf("a:%d\n", a[0]);
printf("e:%d\n", a[1]);
printf("i:%d\n", a[2]);
printf("o:%d\n", a[3]);
printf("u:%d\n", a[4]);
}
return 0;
}
H - 求平均成绩
题解:对于每个学生的平均成绩和没门科目的平均成绩的统计的方式不一样,前者外存循环应按行遍历,内存循环按列遍历,而后者外层循环应按列遍历,内层循环按行遍历,
#include <stdio.h>
#include <string.h>
int main()
{
double a[110][6];
double aver_st[110], aver_cou[6];
int n, m;
while(scanf("%d %d", &n, &m) != EOF){
int i, j;
/**
memset(name, val, size)
此函数是将数组name赋值值为val, 一般用于数组的初始化
**/
memset(aver_st, 0, sizeof aver_st);
memset(aver_cou, 0, sizeof aver_cou);
for(i = 1; i <= n; i ++){
for(j = 1; j <= m; j ++){
scanf("%lf", &a[i][j]);
}
}
/**统计每个学生的平均成绩**/
for(i = 1; i <= n; i ++){///外层按行遍历
for(j = 1; j <= m; j ++){///内层按列遍历
aver_st[i] += a[i][j];
}
aver_st[i] /= (1.0 * m);
printf("%.2f", aver_st[i]);
if(i == n) printf("\n");
else printf(" ");
}
/**统计没门学科的平均成绩**/
for(j = 1; j <= m; j ++){///外层按列遍历
for(i = 1; i <= n; i ++){///内层按行遍历
aver_cou[j] += a[i][j];
}
aver_cou[j] /= (1.0 * n);///的平均分
printf("%.2f", aver_cou[j]);
if(j == m) printf("\n");
else printf(" ");
}
int cnt = 0;
/**统计每门学科的分数都大于等于班级均分的学生个数**/
for(i = 1; i <= n; i ++){
int flag = 1;
for(j = 1; j <= m; j ++){
if(a[i][j] < aver_cou[j]){
flag = 0; break;///当出现有一门学科低于班级均分就跳出循环,并置flag为0
}
}
cnt += flag;
}
printf("%d\n\n", cnt);
}
return 0;
}
I - 查找最大元素
题解:先O(n)遍历字符串, 用Max记录输入字符串的最大字符,再第二次遍历并输出字符,若当前字符是最大字符,在该字符输出之后多输出(max)
#include <stdio.h>
#include <string.h>
const int maxn = 110;
int max(int a, int b){
if(a > b) return a;
return b;
}
int main()
{
char s[maxn];
while(scanf("%s", s) != EOF){
int i, len = strlen(s), Max = -1;
for(i = 0; i < len; i ++){
Max = max(Max, (int)s[i]);
}
for(i = 0; i < len; i ++){
printf("%c", s[i]);
if(Max == (int)s[i]){
printf("(max)");
}
}
printf("\n");
}
return 0;
}
剩下三道题都是CF上的题,英文题面,希望大家习惯,题目难度稍微大一点,重在考察思维
J - Table Tennis
题意:有n个人站成一排进行网球比赛,每个人都有一个能力值,当两个人进行比赛时,能力大的人将会获胜。开始时,第一个人和第二个人进行比赛,赢的人继续与下一个人比赛,战败的人则站到队伍的最后一个人之后,当一个人连续获胜k此时,比赛结束,问:当比赛结束时,获胜的人的能力值是多少? 2<=n<=500, 2<= k <= 1e12, 题目保证每个人的能力值不同,均在1~n之间
题解:本题误解:有人可能认为 获胜的人的能力值就是所有人中能力值最大的,当 k < n 时显然是不成立的,因为能力值最大人可能在后面,比赛还没进行到能力值最大的人就已经结束了。
但当 k >= n 时,其他获胜的人总会遇到能力值最大的人, 能力值最大的人 会一直获胜下去,直至他获胜次数达到k,比赛结束。
当 k < n时, 我们就可以用数组模拟即可,获胜的人继续和下一个人比赛, 而战败的人插入到队伍最后,知道出现第一个连续获胜k次的人为止。 复杂度O(n)
#include <stdio.h>
#include <string.h>
int main()
{
int a[550*2], cnt, n, i, j;
long long k;
scanf("%d %lld", &n, &k);
int Max = -1;
for(i = 1; i <= n; i ++){
scanf("%d", &a[i]); a[i+n] = a[i];
if(a[i] > Max) Max = a[i];
}
if(k >= n) {
printf("%d\n", Max); return 0;
}
int num = 0;
cnt = 0;///记录连续获胜的次数
Max = a[1];
for(i = 2; i <= n + num; i ++){
if(Max > a[i]){
cnt ++;
a[++num] = a[i]; ///战败的人插入到队伍最后
}
else {
a[++num] = Max; Max = a[i]; ///注意着两条语句不能颠倒
cnt = 1;
}
if(cnt >= k) {
printf("%d\n", Max); break;
}
}
return 0;
}
K - Nearly Lucky Number
题意:4和7均为幸运数字, 一个数是幸运数当且顶点该数仅包含数字4或7;一个数是近似幸运数当且仅当该数中幸运数字出现的次数之和是幸运数;判定给定一个数n是否是近似幸运数?1<=n<=1e18
可直接考虑用字符数组读入输入的整数,遍历字符串, 统计幸运数字出现的次数记为x,然后判断x是否是幸运数
复杂度O(n)
#include <stdio.h>
#include <string.h>
int main()
{
int len, i, flag = 1;
char s[100];scanf("%s", s);
len = strlen(s);
int cnt = 0;
for(i = 0; i < len; i ++){
if(s[i] == '4' || s[i] == '7') cnt ++;
}
if(cnt == 0){///当cnt为0时, 表明输入不包含幸运数字,应输出NO
printf("NO\n"); return 0;
}
while(cnt){
// printf("cnt = %d\n", cnt);
if(cnt % 10 != 4 && cnt % 10 != 7){
flag = 0; break;
}
cnt /= 10;
}
if(flag){
printf("YES\n");
}
else printf("NO\n");
return 0;
}
L - Cheap Travel
题意:Ann乘坐地铁,已知一张普通票可以乘坐一站地铁,费用为a元,但Ann可以购买一种特殊的票,Ann可以用此票乘坐m站地铁,花费为b,现在Ann想要乘坐n站地铁,请你帮计算他所需的最小花费
题解:计算特殊票平均每站的花费,将其与乘坐每站的费用a比较,若a或等于则直接购买n张普通票, 答案就是n*a;
否则,Ann可以购买n/m站的特殊票,剩下的n % m 站取 全部购买 普通票和购买一张特殊票的最小值
复杂度O(1)
#include <stdio.h>
int main()
{
int n, m, a, b;scanf("%d %d %d %d", &n, &m, &a, &b);
int ans;
if(m * a <= b){
ans = n * a;
}
else {
ans = (n / m) * b;
if((n%m) * a <= b) ans += (n % m) * a;
else ans += b;
}
printf("%d\n", ans);
return 0;
}