C语言快速入门刷题指南
题目链接
牛客训练3【邀请码:hzu2022】
A、上下取整
#include
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
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
// getchar()即输入一个字符的意思,因为输出字符是不会吃掉回车的,所以需要手动吃掉回车符getchar();for(int j=0;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
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
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
// 因为我们要调用sqrt函数求平方根,所以需要导入math.h这个头文件
#include// 我们之前都是调用系统的函数,像绝对值函数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
// 如果被整除了,那么该数一定不是素数,返回0if(val%i==0)return 0;}
// 如果是素数就返回1return 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
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.....等等它们的值都要更新为1for(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
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循环的条件是判断式的值不等于0while(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
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-12if(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
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
using namespace std;
int main(){int n;vector 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
#include
// 因为要用到平方根函数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
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
// 需要吃掉多余的回车符,不然就被当作数据来输入了getchar();for(int j=0;jchar 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;ifor(int j=0;jif(mi[i]>a[i][j]){mi[i]=a[i][j];}}}
// 默认全部都可以int ans=n;for(int i=0;ibudget-=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
#include
int main(){char s[1060]={0};scanf("%s",s);int flag=0;int n=strlen(s);int j=0;//判断是否有前缀SA,如果有,那么flag的值是2for(int i=0;iif(s[i]=='S')flag=1;if(s[i]=='A'&&flag==1){flag=2;j=i+1;break;}}//没有前缀SAif(flag<2){s[n]='S';s[n+1]='A';s[n+2]='N';s[n+3]='T';s[n+4]='A';}else{//有前缀SAfor(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
int main(){int n;scanf("%d",&n);int a[1010][1010]={0};int d=1;for(int i=0;iif(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;iif(i%2==n%2){int j=i;int k=n-1;while(ja[j][k]=d++;j++;k--;}}else{int j=n-1;int k=i;while(ka[j][k]=d++;j--;k++;}}}for(int i=0;ifor(int j=0;jprintf("%d ",a[i][j]);}printf("\n");}
}
M、回型矩阵
#include
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;i--){a[d][i]=cnt++;}d--;if(d=u;i--){a[i][l]=cnt++;}l++;if(l>r)break;}for(int i=0;ifor(int j=0;jprintf("%d ",a[i][j]);}printf("\n");}
}
N、约瑟夫环
这里用到了c++的的数据结构,queue,即队列,就是简单的排队,有出队操作pop()和入队操作push()
例如
3入队了,目前3
5入队了,目前3、5
2入队了,目前3、5、2
有人出队了,目前,5、2
即入队一定排在后面,出队一定排在前面
size()可以查看队列中目前有多少个元素
#include
#include
using namespace std;
int main(){queue q;int n,k,m;scanf("%d%d%d",&n,&k,&m);for(int i=0;iq.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
#include
int main(){char a[128]={0};char b[60],c[60];scanf("%s%s",b,c);int n=strlen(b);for(int i=0;ia[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;iif(a[s[i]]==0){f=0;break;}else{s[i]=a[s[i]];}}if(f){printf("%s",s);}else{printf("@");}
}
P、牛牛的星际旅行
这个题意一塌糊涂,全靠猜的,就是求n减去两个数字重复出现的间隔再加1
#include
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(ansans=n-(i-m[a[i]])+1;}}}printf("%d\n",ans);
}
Q、StringGame
#include
int main(){
// 注意要用long longlong 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;is2[d++]=s[i%n];}printf("%s",s2);
}