编程初级训练营复盘
Posted 君違
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编程初级训练营复盘相关的知识,希望对你有一定的参考价值。
这次是编程初级训练营(牛客网)的简单复盘,选了一些平时学习时没注意到的小知识点和一些比较有意思的题目。
如果有人会小乐乐和二段数的话请务必教教我!
话不多说,Let’s go!
今天题目的娇容
BC14 出生日期的输入输出(%d的奇妙补零)
如果你想将月份输成高端大气自带补零的样子,该怎么办?
手动补? 0%d? 那么12呢?
还好c够体贴,它已经为你考虑好了。
你只要使用%02就行了!买不了吃亏,买不了上当。
千万不要像我这个笨比一样用上case语句。
scanf("%4d%2d%2d",&a,&b,&c);
printf("year=%d\\nmonth=%02d\\ndate=%02d",a,b,c);
BC45 最高分数(scanf,~与EOF的三角爱情)
牛客网经常出现输入几组数的情况,如果我们熟悉scanf这个函数,那么就可以癞蛤蟆找青蛙——长得丑玩的花。
上代码:
while (scanf("%d %d %d", &a, &b, &c) != EOF)
肯定会有人在说,哎呀!=EOF好长啊,有没有更嚣张的写法呢?
当然有!
while (~scanf("%d %d %d", &a, &b, &c))
看到这个小小的~没有,它其实是和!=EOF是一个意思。
那么原因呢?
我们先要从scanf的返回值说起:
scanf的返回值取决于成功赋值的项数,比如
scanf("%d %d %d", &a, &b, &c) //这里的返回值是3
scanf("%d", &a) //这里是1
那么如果很不幸,什么都没捞到,返回值是多少呢?
是-1,也就是EOF。(EOF要引stdio头文件)
而~是按位取反。(这个可能以后会补充,有兴趣的可以查查)
而只有-1按位取反的值是0,也就是while(0),跳出循环。
但是我们实际操作的时候可能遇到问题。
它 停 不 下 来 了 !
因 为 没 人 告 诉 它 什 么 时 候 结 束!
在win10 系统里 快捷键ctrl c可以告诉系统到了文件末尾,这样就可以结束循环了。
BC69,70 空心正方形和空心三角形
话不多说,上图。
刚开始,我觉着把第一行和最后一行打印,最后按规律打印中间的图案。
不就是找规律吗,谁不会啊?
那可不可以把第一行和最后一行弄进循环里面呢?
显然可以。
我们令行为i,列为j,正方形的边长为n,然后开始找规律。
在正方形里,第0行,第n-1行永远是*,
而第0列,第n-1列永远是*。
那么——
我们就把这些全部变成 * !
#include <stdio.h>
int main()
{
int n = 0;
while (scanf("%d", &n) != EOF)
{
for (int i=0; i<n; i++)
{
for (int j=0; j<n; j++)
{
if (i == 0 || i == n-1 || j == 0 || j == n-1)
printf("* "); //把0行,n-1行,0列,n-1列都改成0
else
printf(" ");
}
printf("\\n");
}
}
return 0;
}
同理,三角形只要当j=i时放入一个*并且限制一下每一行的长度就可以了。
#include<stdio.h>
int main()
{
int n = 0;
while (scanf("%d", &n) != EOF)
{
for (int i=0; i<n; i++)
{
for (int j=0; j<=i; j++)
{
if (i == n-1 || j == 0 || j == i)
printf("* "); //把最后一行,第0列和对角线变成*
else
printf(" ");
}
printf("\\n");
}
}
return 0;
}
BC77 有序序列插入一个数
这题比较简单就讲讲思路。
把这放在第一题主要是和其他序列题进行对比。
如果我们想要在这个序列中插入一个10.
我们就把10后面的数字统统往后移一位,再把10填进去。(数组一定要足够大!)
for(i=0;i<n;i++)
{
if(num>arr[i])
{
insert=i+1; //找到插入位置
}
}
for(j=n;j>=insert;j--) //往后循环一直到insert插入的位置
{
arr[j+1]=arr[j]; //往后赋值,空出一个位置给n插入
}
arr[insert]=num;
BC96 有序序列判断
首先我们先判断什么是有序序列。
1 2 3 4 5 //是
5 3 2 1 //是
1 1 2 5 6 7 //是
1 2 3 5 4 //不是
我们会发现,在第三个序列中,混入了两个讨厌的1,在编程的时候,就得把这种特殊情况考虑进去。
那么理一下思路:
首先分为两块:正序,倒序
然后分别判断是否有序。
先看我写的第一段代码:(这段代码是有问题的!)
for(i=0;i<a;i++)
{
scanf("%d",&arr[i]);
}
if(arr[0]<=arr[1]) //考虑两数相同的情况
{
for(i=0;i<a-1;i++)
if(arr[i]>arr[i+1])//判断是否为有序序列
{
printf("unsorted");
goto end;
}
goto p;
}
if(arr[0]>=arr[1]) //考虑两数相同的情况
{
for(i=0;i<a-1;i++)
if(arr[i]<arr[i+1]) //判断是否为有序序列
{
printf("unsorted");
goto end;
}
goto p;
}
p:printf("sorted");
end:return 0;
把这段代码扔到牛客网上,是可以过的,但是不代表它没有问题。
让我们输入 5
5 5 4 3 2
结果是什么? unsortd。
为什么?因为它进入了第一个if,然后直接goto end语句,丝毫没有考虑后面的倒序if。而且不到万不得已,千万不要使用goto!
倒序和正序都要考虑,如何让电脑知道已经符合了条件呢?
可以加两个变量f1,f2,初始化为1。
我们可以再理一下思路:倒序正序都要看,就是要走两个循环。
只要不是顺序,f变为0.
两个f都为0就不为顺序。
代码如下:
for(i=0;i<a;i++)
{
scanf("%d",&arr[i]);
}
for(i=0;i<a-1;i++)
{
if(arr[i]>arr[i+1])
{
f1=0;//不是正序,f1变为0
}
}
for(i=0;i<a-1;i++)
{
if(arr[i]<arr[i+1])
{
f2=0;//不是倒序,f2变为0
}
}
if(f1||f2)//f1,f2有一个为1就是顺序
printf("sorted");
else
printf("unsorted");
return 0;
}
BC98 序列中删除指定数字
这一题我觉得好玩的地方在于说是删除指定数字,但是,欸,就没删。
通过不打印来造成数字被删掉的假象。
是不是有想法了?
for(i=0;i<a;i++)
{
scanf("%d",&arr[i]);
}
scanf("%d",&b);//要删除的数字
for(i=0;i<a;i++)
{
if(arr[i]!=b)
{
printf("%d ",arr[i]);//打印除了b以外的所有数字
}
}
当然把要删除的数字改成0也是可以的,但没有这个方法简便。
BC99,136 序列中整数去重,去重并排序
整数去重
第一题可以把要删除的整数改成0,打印非零的数。
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
for(i=0;i<n;i++)
{
for(j=i+1;j<n;j++)//从i后面的一个数开始
{
if(a[i]==a[j])//将重复的改为0
a[j]=0;
}
}
for(i=0;i<n;i++)
{
if(a[i]!=0)
{
printf("%d ",a[i]);//打印非零的数
}
}
整数去重并排序
先看看题目:
第一行,输入一个整数n,表示序列有n个整数。
第二行输入n个整数(每个整数大于等于1,小于等于1000),整数之间用空格分隔。
我们可以从这个整数的范围入手。
构建一个1001大小的数组,此时数组的元素下标是[0],[1],[2]…[1000]。
如果我们把下标为这些要处理的整数的值赋值为1,
比如 5 5 2 3 1
令arr[5]=1 arr[2]=1 arr[3]=1 arr[1]=1
再按顺序打印值为1的i的值,是不是又做到了既去重又排了序呢?
scanf("%d",&a);
while(a--)
{
scanf("%d",&i);
arr[i]=1;//令以这些整数为名字的数组下标的值为1
}
i=0;
for(i=0;i<1001;i++)
{
if(arr[i]==1)
{
printf("%d ",i);//打印值为1的数组下标
}
}
(可能我的说法不太严谨,可以在评论区告诉我正确的说法)
BC116 小乐乐改数字
题目:
小乐乐喜欢数字,尤其喜欢0和1。他现在得到了一个数,想把每位的数变成0或1。如果某一位是奇数,就把它变成1,如果是偶数,那么就把它变成0。请你回答他最后得到的数是多少。
最初思路:将数字存到数组里面,接着转换,最后输出。
问题:前面的0怎么办?——>检测到1后输出——>如果结果是0检测不到1怎么办——>使最后一个数在循环之外。
代码(ver1.0):
while(a)//a是输入的数
{
int b=a%10;//倒着存储
a/=10;
if(b%2==0)
{
arr[c]=0;
c++;
}
else
{
arr[c]=1;
c++;
}
}
for(i=c-1;i>0;i--)//倒着打印
{
if(arr[i]==1)//找到第一个1
{
while(i)
{
printf("%d",arr[i]);
i--;
}
}
}
printf("%d",arr[0]);//打印最后一个数
显然,这个和上一题一样,过于复杂了。
下面我们来简化代码:
设置一个变量k,一开始为1,每一次循环10.
一个变量sum,每一次循环的值是n%10k+sum。
每次循环结束n/10。
代码(ver2.0)
scanf("%d",&n);
for(k=1;n;k=k*10)
{
t=n%10;//取出最后一个数
if(t%2==0)
x=0;
else
x=1;
sum=x*k+sum;
n=n/10;
}
printf("%d",sum);
BC117 小乐乐走台阶(也就是青蛙跳台阶问题)
题目:
一只青蛙 小乐乐上课需要走n阶台阶,因为他腿比较长,所以每次可以选择走一阶或者走两阶,那么他一共有多少种走法?
非常经典的递归题。
我们可以先拆分:
拆分剩下的不是2就是1
如果是2 有两种走法:1+1和2
如果是1 只有有一种走法:就是直接走一步。
就可以写一个递归函数了。
int t(int x)
{
if (x<=2)
return x;
return t(x-1)+t(x-2);
}
再写个主函数:
int main()
{
int n=0;
scanf("%d",&n);
int x=t(n);
printf("%d",x);
return 0;
}
本题解决。
BC119 小乐乐与字符串
题目:
在庆祝祖国母亲70华诞之际,老师给小乐乐出了一个问题。大家都知道China的英文缩写是CHN,那么给你一个字符串s,你需要做的是统计s中子串“CHN”的个数。
子串的定义:存在任意下标a < b < c,那么“s[a]s[b]s[c]”就构成s的一个子串。如“ABC”的子串有“A”、“B”、“C”、“AB”、“AC”、“BC”、“ABC”。
输入描述:
输入只包含大写字母的字符串s。(1 ≤ length ≤ 8000)
输出描述:
输出一个整数,为字符串s中字串“CHN”的数量。
输入:
CCHNCHN
输出:
7
这一题主要是题干有点难懂,需要多读几遍。
我们依旧可以画图解决:
这 7 种 是 这 样 算 的!
反正一开始我没想到。
这个图可以更直观一点。
我们先设置c,ch,chn三个变量,初始化为0。
遇到c的时候,c++,遇到h的时候ch的值就是ch+c,表明有这么多个ch,遇到n的时候chn的值就是chn+ch。
最后上代码:
while(arr[i])
{
if(arr[i]=='C')
c++;
else if(arr[i]=='H')
ch+=c;
else if(arr[i]=='N')
chn+=ch;
i++;
}
printf("%lld\\n",chn);
结尾
到这里复盘就结束了,如果还有什么问题(二段数除外,毕竟我菜)可以评论区提问,我会尽量解答或补充!
爱你们。
以上是关于编程初级训练营复盘的主要内容,如果未能解决你的问题,请参考以下文章
[AndroidStudio]_[初级]_[配置自动完成的代码片段]
复盘8.3训练:Nordic Collegiate Programming Contest 2020
[AndroidStudio]_[初级]_[配置自动完成的代码片段]