牛客训练3

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家餐馆,每天的花费如下:

天数第一家第二家第二家
1836
2343
3754
4698
5137
6296
7223
8702
9335
10216
11221
12762

我们试着做一个前缀和,即
第8天的花费需要加上第1天的花费
第9天的花费需要加上第2天的花费
第10天的花费需要加上第3天的花费
第11天的花费需要加上第4天的花费
第12天的花费需要加上第5天的花费

天数第一家第二家第二家
1836
2343
3754
4698
5137
6296
7223
81538
9678
109610
118119
1281510

然后求一下每天的最小花费

天数第一家第二家第二家最小花费
18363
23433
37544
46986
51371
62962
72232
815383
96786
1096106
1181198
12815108

如果吃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);
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旧林墨烟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值