C语言快速入门刷题指南
题目链接
牛客训练3【邀请码:hzu2022】
A、上下取整
#include<stdio.h>
int main(){
double a;
scanf("%lf",&a);
// 方法一:ceil()、floor(),flool()是向下取整函数,ceil()是向上取整函数
// printf("%d\n%d",(int)floor(a),(int)ceil(a));
// 方法二:判断a是不是整数
// 如果a是整数,则上下取整都是a
// 如果不是整数,则向下取整是a,向上取整是a+1
// 那么浮点数如何判断相等呢?
// 下面这个判断比较容易理解,(int)a是将a向下取整的意思
// 如果a本来就是整数,那么向下取整之后仍然等于自己
// 如果不是,那么向下取整之后肯定不等于本身
if(a==(int)a){
printf("%d\n%d",(int)a,(int)a);
}else{
printf("%d\n%d",(int)a,(int)(a+1));
}
// 但是判断浮点数相等用上面的判断方式是错误的,因为double类型只能保证小数点后12位是准确的
// 浮点数经过运算后只能取近似值,像1.0/3=0.333333....它只能取个大概的值,所以浮点数并不能保证数值的准确性
// 所以,在计算机里面,double类型的两个数的差值只要不超过1e-12,即10的-12次方,即0.000000000001,那么就认为这两个数是相等的
// fabs是c语言取绝对值的一个函数,下面表达的意思就是,a-(int)a的绝对值小于10的12次方
// 我们以后判断浮点数相等都应该使用这种方式比较
// if(fabs(a-(int)a)<=1e-12){
// printf("%d\n%d",(int)a,(int)a);
// }else{
// printf("%d\n%d",(int)a,(int)(a+1));
// }
}
B、牛牛VS牛妹
#include<stdio.h>
int main(){
// 博弈题,可以推导出,无论怎么走,最后剩余的点的数量都是固定的,即n+m-1个
int n,m;
char a[30][30]={0};
scanf("%d%d",&n,&m);
int sum=0;
for(int i=0;i<n;i++){
// getchar()即输入一个字符的意思,因为输出字符是不会吃掉回车的,所以需要手动吃掉回车符
getchar();
for(int j=0;j<m;j++){
// 用getchar()输入是一样的,下面是两种输入方法
a[i][j]=getchar();
// scanf("%c",&a[i][j]);
// 统计字符#的数量
if(a[i][j]=='#'){
sum++;
}
}
}
// 总共n*m个格子,减去sum,即字符#的数量,剩下的就是点的数量了
// 再减去剩余点数量(n+m-1),就是可操作的步数了
// 如果可操作步数是奇数,那么就是niuniu赢,否则就是niumei赢
if((n*m-sum-n-m+1)%2==1){
printf("niuniu\n");
}else{
printf("niumei\n");
}
}
C、箱子归位
#include<stdio.h>
int main(){
// 考察曼哈顿距离,移动的步数就是曼哈顿距离,公式如下
// d(i,j)=|x1-x2|+|y1-y2|
// 养成好习惯,开数组时开多几个,避免粗心遇到边界问题
int a[10][10]={0};
int ans=0;
for(int i=0;i<5;i++){
for(int j=0;j<5;j++){
scanf("%d",&a[i][j]);
// abs是取绝对值函数,如果出现了1,直接计算,第三行第三列对应的坐标是(2,2)
if(a[i][j]==1){
ans=abs(i-2)+abs(j-2);
}
}
}
printf("%d",ans);
}
D、牛牛学数列7
#include<stdio.h>
int main(){
// 斐波那契数列满足,第i项的值等于i-1项的值加上i-2项的值,从样例可以看出来
int n;
scanf("%d",&n);
long long a[60]={0,1,1};
for(int i=3;i<=n;i++) {
a[i]=a[i-1]+a[i-2];
}
printf("%lld",a[n-1]);
}
E、打印质数表
方法一 暴力枚举
#include<stdio.h>
// 因为我们要调用sqrt函数求平方根,所以需要导入math.h这个头文件
#include<math.h>
// 我们之前都是调用系统的函数,像绝对值函数abs(),向上取整函数ceil()
// 输入函数scanf(),输出函数printf(),其实main也是以函数的方式出现的
// 我们为什么能用printf()函数呢?因为我们导入别人写好的一个函数库stdio.h
// 我们自己也可以写函数,自己调用
int prime(int val){
// 1-3的值需要特判,因为我们循环判断只判断到sqrt(val),1-3是判断不了的
if(val==1)return 0;
if(val==2||val==3)return 1;
// 为什么只要判断到sqrt(val)?
// 例如一个数8,它的因数有1、2、4、8,可以发现1<2<sqrt(8)<4<8
// 所以我们只要判断小于sqrt(8)的数就可以了,因为因数都是成对出现的
for(int i=2;i<=sqrt(val);i++){
// 如果被整除了,那么该数一定不是素数,返回0
if(val%i==0)return 0;
}
// 如果是素数就返回1
return 1;
}
int main(){
int n;
scanf("%d",&n);
for(int i=2;i<=n;i++){
// 如果返回值是1,那么该数是素数,输出即可
if(prime(i)==1){
printf("%d ",i);
}
}
}
方法二 素数筛
1、用一个数组表示一个数是不是素数,如果它的值是0,那么就代表是素数,1就代表不是素数
2、如果2是素数,那么4、6、8、10…等等2的倍数就不可能是素数,我们就把它们的值更新为1
如果3是素数,那么6、9、12、15、18…就不可能是素数,我们就把它们的值更新为1
4已经是素数,8、16…已经包括在2的倍数里面了,没必要更新重复更新后面的值
5没被标记成1,那么5就是素数,把10、15、20、15…的值更新为1
后面的都是如此处理
3、素数筛的时间复杂度很难算,但是大概是nlog(n),比暴力算法快了一个指数级
ps:时间复杂度是对程序运行的次数的一个评估,如果输入的n是100次,那么运行的次数就是100*log(2,100)
#include<stdio.h>
int main(){
int n;
scanf("%d",&n);
int f[2010]={1,1};
for(int i=2;i<=2000;i++){
// 如果目前的数不是素数,直接continue跳过这个数的更新
if(f[i]==1)continue;
// 如果这个数是2,那么4、6、8、10.....等等它们的值都要更新为1
for(int j=2;i*j<=2000;j++){
f[i*j]=1;
}
}
for(int i=2;i<=n;i++){
// 如果是f[i]的值等于0就代表i是素数
if(f[i]==0){
printf("%d ",i);
}
}
}
F、字符统计
#include<stdio.h>
int main(){
int letter=0;
int digits=0;
int others=0;
char c;
// 括号表达式,例如(a,b,c),从左到右依次执行a、b、c,但是返回值是最后一个,即c
// 下面先执行scanf("%c",&c),再执行c!='?',最后判断c!='?'
// 如果判断成立,那么表达式的值就是1,否则就是0,while循环的条件是判断式的值不等于0
while(scanf("%c",&c),c!='?'){
// acsii码字母、数字的值分别都是连续的,可以像以下这样使用
if(c>='a'&&c<='z'||c>='A'&&c<='Z'){
letter++;
}else if(c>='0'&&c<='9'){
digits++;
}else{
others++;
}
}
printf("Letters=%d\n",letter);
printf("Digits=%d\n",digits);
printf("Others=%d\n",others);
}
G、选村长
#include<stdio.h>
int main(){
int a=0;
int b=0;
int c=0;
int sum=0;
int sum2=0;
// val即value缩写
int val;
while(scanf("%d",&val),val!=-1){
sum2++;
if(val==1){
a++;
sum++;
}else if(val==2){
b++;
sum++;
}else if(val==3){
c++;
sum++;
}
}
printf("A=%d\n",a);
printf("B=%d\n",b);
printf("C=%d\n",c);
printf("Tot=%d\n",sum);
// 转换一下题意,因为a>sum2/2会出现取整问题,所以将其·转换为乘法判断
// 你也可以复习一下浮点数的判断,记得加上abs(a-sum2/2)<1e-12
if(a*2>sum2){
printf("A-yes\n");
}else if(b*2>sum2){
printf("B-yes\n");
}else if(c*2>sum2){
printf("C-yes\n");
}else{
printf("all-NO\n");
}
}
H、回文对称数
方法一 暴力模拟
一个数如果是会问对称数,那么它的各位倒转过来仍然等于本身
例如 :
1040倒转后是0401,不等于本身,所以不是
54045倒转后仍然是54045,所以是回文对称数
#include<stdio.h>
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
int a=i;
int b=0;
// 将a的数值倒转过来,如果还等于本身,则该数字是回文数字
while(a){
b*=10;
b+=a%10;
a/=10;
}
if(b==i){
printf("%d\n",i);
}
}
}
方法二 直接构造
1可以构造1、11
2可以构造 2、22
3…
10可以构造101、1001
11可以构造 111、111
12可以构造 121、1221
一直构造到第一个构造数大于n停止,这样是最快的
但是构造得到的数据是乱序的,所以存完数据后需要排序,但是用冒泡排序复杂度会提高一大截,所以我们c++自带的sort()函数做了,下面代码不要求掌握,想看就看看吧
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
vector<int> v;
scanf("%d",&n);
for(int i=1;;i++){
int a=i;
int b=0;
int sum=0;
while(a){
sum++;
b*=10;
b+=a%10;
a/=10;
}
a=i;
a*=pow(10,sum);
a+=b;
if(a<=n){
v.emplace_back(a);
}
sum--;
a=i;
a*=pow(10,sum);
b%=(int)pow(10,sum);
a+=b;
if(a<=n){
v.emplace_back(a);
}else{
break;
}
}
sort(v.begin(),v.end());
for(int c:v){
printf("%d\n",c);
}
}
I、平方根
#include<stdio.h>
#include<math.h>
// 因为要用到平方根函数sqrt(),所以要导入这个头文件
int main(){
int n;
scanf("%d",&n);
printf("%d\n",(int)sqrt(n));
}
J、神秘餐馆
这题有点竞赛难度的,建议花几个小时把它学懂,会有收获的
理解:假如有12天,3家餐馆,每天的花费如下:
天数 | 第一家 | 第二家 | 第二家 |
---|---|---|---|
1 | 8 | 3 | 6 |
2 | 3 | 4 | 3 |
3 | 7 | 5 | 4 |
4 | 6 | 9 | 8 |
5 | 1 | 3 | 7 |
6 | 2 | 9 | 6 |
7 | 2 | 2 | 3 |
8 | 7 | 0 | 2 |
9 | 3 | 3 | 5 |
10 | 2 | 1 | 6 |
11 | 2 | 2 | 1 |
12 | 7 | 6 | 2 |
我们试着做一个前缀和,即
第8天的花费需要加上第1天的花费
第9天的花费需要加上第2天的花费
第10天的花费需要加上第3天的花费
第11天的花费需要加上第4天的花费
第12天的花费需要加上第5天的花费
天数 | 第一家 | 第二家 | 第二家 |
---|---|---|---|
1 | 8 | 3 | 6 |
2 | 3 | 4 | 3 |
3 | 7 | 5 | 4 |
4 | 6 | 9 | 8 |
5 | 1 | 3 | 7 |
6 | 2 | 9 | 6 |
7 | 2 | 2 | 3 |
8 | 15 | 3 | 8 |
9 | 6 | 7 | 8 |
10 | 9 | 6 | 10 |
11 | 8 | 11 | 9 |
12 | 8 | 15 | 10 |
然后求一下每天的最小花费
天数 | 第一家 | 第二家 | 第二家 | 最小花费 |
---|---|---|---|---|
1 | 8 | 3 | 6 | 3 |
2 | 3 | 4 | 3 | 3 |
3 | 7 | 5 | 4 | 4 |
4 | 6 | 9 | 8 | 6 |
5 | 1 | 3 | 7 | 1 |
6 | 2 | 9 | 6 | 2 |
7 | 2 | 2 | 3 | 2 |
8 | 15 | 3 | 8 | 3 |
9 | 6 | 7 | 8 | 6 |
10 | 9 | 6 | 10 | 6 |
11 | 8 | 11 | 9 | 8 |
12 | 8 | 15 | 10 | 8 |
如果吃1天,至少花费3
如果吃2天,至少花费3+3
如果吃3天,至少花费3+3+4
如果吃4天,至少花费3+3+4+6
如果吃5天,至少花费3+3+4+6+1
如果吃6天,至少花费3+3+4+6+1+2
如果吃7天,至少花费3+3+4+6+1+2+2
如果吃8天,至少花费3+3+4+6+1+2+3-3
如果吃9天,至少花费3+3+4+6+1+2+3+6-3-3
如果吃10天,至少花费3+3+4+6+1+2+3+6+6-3-3-4
如果吃11天,至少花费3+3+4+6+1+2+3+6+6+8-3-3-4-6
如果吃12天,至少花费3+3+4+6+1+2+3+6+6+8+8-3-3-4-6-1
为什么要减呢?因为第八天最小花费的含义已经包括了第一天的
同理,第九天的也涵盖了第二天的花费
#include<stdio.h>
int main(){
int n,m,budget;
scanf("%d%d%d",&n,&m,&budget);
int a[60][60]={0};
int mi[60]={0};
// 给它初始化一个足够大的数
for(int i=0;i<60;i++){
mi[i]=100000000;
}
for(int i=0;i<n;i++){
// 需要吃掉多余的回车符,不然就被当作数据来输入了
getchar();
for(int j=0;j<m;j++){
char c;
scanf("%c",&c);
if(c>='0'&&c<='9'){
a[i][j]=c-'0';
}else if(c>='A'&&c<='Z'){
a[i][j]=c-'A'+10;
}else{
a[i][j]=c-'a'+36;
}
// 前缀和
if(i-7>=0){
a[i][j]+=a[i-7][j];
}
}
}
// 计算同一天选择那个餐馆的最小值
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(mi[i]>a[i][j]){
mi[i]=a[i][j];
}
}
}
// 默认全部都可以
int ans=n;
for(int i=0;i<n;i++){
budget-=mi[i];
if(i-7>=0){
budget+=mi[i-7];
}
if(budget<0){
ans=i;
break;
}
}
printf("%d",ans);
}
K、字符串构造
题目意思不太清晰,就是构造一个字符串不能含有子序列"SATAN"但要含有
子序列"SANTA",子序列:可以删除任意字符得到的字符串例如ABCDEFG,删除AC得到子序列BDEFHG。
那么怎样保证构造出"SANTA"但是不含有"SATAN"呢?两种情况
1、如果没有前缀SA,那么直接在后面添加"SANTA"
2、如果有前缀SA,那么在第一个SA后面添加N,在字符串末尾加上TA就可以了
#include<stdio.h>
#include<string.h>
int main(){
char s[1060]={0};
scanf("%s",s);
int flag=0;
int n=strlen(s);
int j=0;
//判断是否有前缀SA,如果有,那么flag的值是2
for(int i=0;i<n;i++){
if(s[i]=='S')flag=1;
if(s[i]=='A'&&flag==1){
flag=2;
j=i+1;
break;
}
}
//没有前缀SA
if(flag<2){
s[n]='S';
s[n+1]='A';
s[n+2]='N';
s[n+3]='T';
s[n+4]='A';
}else{
//有前缀SA
for(int k=n+1;k>=1+j;k--){
s[k]=s[k-1];
}
s[j]='N';
s[n+1]='T';
s[n+2]='A';
}
printf("%s",s);
}
L、蛇形矩阵
模拟过程
#include<stdio.h>
int main(){
int n;
scanf("%d",&n);
int a[1010][1010]={0};
int d=1;
for(int i=0;i<n;i++){
if(i%2==0){
int j=i;
int k=0;
while(j>=0){
a[j][k]=d++;
j--;
k++;
}
}else{
int j=0;
int k=i;
while(k>=0){
a[j][k]=d++;
j++;
k--;
}
}
}
for(int i=1;i<n;i++){
if(i%2==n%2){
int j=i;
int k=n-1;
while(j<n){
a[j][k]=d++;
j++;
k--;
}
}else{
int j=n-1;
int k=i;
while(k<n){
a[j][k]=d++;
j--;
k++;
}
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
printf("%d ",a[i][j]);
}
printf("\n");
}
}
M、回型矩阵
#include<stdio.h>
int main(){
int n;
scanf("%d",&n);
int l=0,r=n-1,u=0,d=n-1;
int cnt=1;
int a[30][30]={0};
while(true){
for(int i=l;i<=r;i++){
a[u][i]=cnt++;
}
u++;
if(u>d)break;
for(int i=u;i<=d;i++){
a[i][r]=cnt++;
}
r--;
if(r<l)break;
for(int i=r;i>=l;i--){
a[d][i]=cnt++;
}
d--;
if(d<u)break;
for(int i=d;i>=u;i--){
a[i][l]=cnt++;
}
l++;
if(l>r)break;
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
printf("%d ",a[i][j]);
}
printf("\n");
}
}
N、约瑟夫环
这里用到了c++的的数据结构,queue,即队列,就是简单的排队,有出队操作pop()和入队操作push()
例如
3入队了,目前3
5入队了,目前3、5
2入队了,目前3、5、2
有人出队了,目前,5、2
即入队一定排在后面,出队一定排在前面
size()可以查看队列中目前有多少个元素
#include<stdio.h>
#include<queue>
using namespace std;
int main(){
queue<int> q;
int n,k,m;
scanf("%d%d%d",&n,&k,&m);
for(int i=0;i<n;i++){
q.push(i);
}
while(k--){
int c=q.front();
q.pop();
q.push(c);
}
int t=0;
while(q.size()!=1){
t++;
if(t%m==0){
q.pop();
}else{
int c=q.front();
q.pop();
q.push(c);
}
}
printf("%d",q.front());
}
O、字符串编码与解码
#include<stdio.h>
#include<string.h>
int main(){
char a[128]={0};
char b[60],c[60];
scanf("%s%s",b,c);
int n=strlen(b);
for(int i=0;i<n;i++){
a[c[i]]=b[i];
}
int sum=0;
char c1,c2;
int g[128]={0};
for(int i='A';i<='Z';i++){
if(a[i]==0){
sum++;
c1=i;
}
g[a[i]]=1;
}
for(int i='A';i<='Z';i++){
if(g[i]==0){
c2=i;
}
}
if(sum==1){
a[c1]=c2;
}
char s[60];
scanf("%s",s);
int m=strlen(s);
int f=1;
for(int i=0;i<m;i++){
if(a[s[i]]==0){
f=0;
break;
}else{
s[i]=a[s[i]];
}
}
if(f){
printf("%s",s);
}else{
printf("@");
}
}
P、牛牛的星际旅行
这个题意一塌糊涂,全靠猜的,就是求n减去两个数字重复出现的间隔再加1
#include<stdio.h>
int main(){
int n;
scanf("%d",&n);
int a[5010]={0};
int m[5010]={0};
int ans=0;
for(int i=1;i<=2*n;i++){
scanf("%d",&a[i]);
if(m[a[i]]==0){
m[a[i]]=i;
}else{
if(ans<n-(i-m[a[i]])+1){
ans=n-(i-m[a[i]])+1;
}
}
}
printf("%d\n",ans);
}
Q、StringGame
#include<stdio.h>
int main(){
// 注意要用long long
long long n,x;
scanf("%lld%lld",&n,&x);
// 移动的次数大于n后,就会出现循环,所以x需要对n取模
x%=n;
char s[100010]={0};
scanf("%s",s);
char s2[100010]={0};
int d=0;
for(int i=x;i<x+n;i++){
s2[d++]=s[i%n];
}
printf("%s",s2);
}