算法竞赛入门经典笔记—第三章

这篇博客介绍了C语言中数组的声明与使用,包括如何复制数组,以及如何利用memcpy函数进行元素迁移。此外,讨论了算法竞赛中的开灯问题和竖式问题,并讲解了C语言中的字符型、字符串处理函数,如scanf、strlen、sprintf等。还探讨了读取带空格字符串的方法,如fgetc和fgets。最后,列举了一系列的算法竞赛题目,涵盖了字符串处理、环状序列、DNA序列分析等多个方面。
摘要由CSDN通过智能技术生成
#include<stdio.h>
#define maxn 105
int a[maxn];//数组一般会声明得稍大一些,防止越界
int main(){
   
    int x, n = 0;
    while(scanf("%d",&x) ==1){
   //当输入不为int型数字时停止
        a[n++] = x;
        //n++先用n计算表达式,再将n加1;++n先将n加1,再计算表达式。
    }
    for(int i = n-1; i >= 1; i--){
   
        printf("%d ", a[i]);
    }
    printf("%d\n",a[0]);
    return 0;
}

用语句“int a[maxn]”声明一个包含maxn个整型变量的数组,即a[0],a[1],…,a[maxn-1],但不包含a[maxn]。maxn必须是常数,不能是变量。只有“int a[maxn]”在放外面时,数组a才可以开得很大。
数组不能够进行赋值操作,如果要从数组a复制k个元素到数组b,可以用:memcpy(b,a,sizeof(int)*k)(需要包含头文件string.h),如果要把数组a全部复制到数组b中,可以用:memcpy(b,a,sizeof(a)).

        开灯问题: 有n盏灯,编号为1~n。第1个人把所有灯打开,第2个人按下所有编号为2的倍数的开关(这些灯将被关掉),第3个人按下所有编号为3的倍数的开关(其中关掉的灯将被打开,开着的灯将被关闭),依此类推。一共有k个人,问最后有哪些灯开着?输入n和k,输出开着的灯的编号。k≤n≤1000。

#include <stdio.h>
#include <string.h>
#define maxn 1005
int a[maxn];
int main() {
   
    int n,k,first = 1;

    memset(a,0, sizeof(a));//把数组a清零

    scanf("%d%d", &n, &k);
    for(int i = 1; i<=k; i++){
   
        for (int j = i; j <=n ; j+=i) {
   
            a[j] = (a[j]+1)%2;//0变1,1变0,这样省去了用if判断
        }
    }
    for (int j = 1; j <=n ; j++) {
   
        if(a[j]==1){
   
            if(first == 1)
            //标志变量first表示当前要输出的变量是否为第一个。第一个变量前不应有空格,但其他变量都有。
                first =0;
            else
                printf(" ");
            printf("%d",j);
        }
    }
    return 0;
}

在很多情况下,最好是在做一件事之前检查是不是可以做,而不要做完再后悔。因为“悔棋”往往比较麻烦。
C语言中的字符型用关键字char表示,它实际存储的是字符的ASCII码。字符常量可以用单引号法表示。在语法上可以把字符当作int型使用。
“scanf("%s", s)”会读入一个不含空格、TAB和回车符的字符串,存入字符数组s。注意s前面没有“&”符号。如果是字符串数组chars[maxn] [maxl],可以用“scanf("%s", s[i])”读取第i个字符串。

可以用sprintf把信息输出到字符串,用法和printf、fprintf类似。但应当保证字符串足够大,可以容纳输出信息。
C语言中的字符串是以“\0”结尾的字符数组,可以用strlen(s)返回字符串s中结束标记之前的字符个数。
竖式问题: 找出所有形如abc*de(三位数乘以两位数)的算式,使得在完整的竖式中,所有数字都属于一个特定的数字集合。输入数字集合(相邻数字之间没有空格),输出所有竖式。每个竖式前应有编号,之后应有一个空行。最后输出解的总数。

#include<stdio.h>
#include<string.h>
int main()
{
   
    int count = 0;
    char s[20], buf[99];
    scanf("%s", s);
    for(int abc = 111; abc <= 999; abc++)
        for(int de = 11; de <= 99; de++)
        {
   
            int x = abc*(de%10), y = abc*(de/10), z = abc*de;
            sprintf(buf, "%d%d%d%d%d", abc, de, x, y, z);
            int ok = 1;
            for(int i = 0; i < strlen(buf); i++)
                if(strchr(s, buf[i]) == NULL)
                    ok = 0;//strchr的作用是在一个字符串中查找单个字符
            if(ok)
            {
   
                printf("<%d>\n", ++count);
                printf("%5d\nX%4d\n-----\n%5d\n%4d\n-----\n%5d\n\n", abc, de, x, y, z);
            }
        }
    printf("The number of solutions = %d\n", count);
    return 0;
}

“scanf("%s")”可以输入字符串,但它碰到空格或者TAB就会停下来。虽然下次调用时会输入下一个字符串,可是不知道两次输入的字符串中间有多少个空格、TAB甚至换行符。
两种方法读取带空格、TAB或换行符的字符串:
      1.  “fgetc(fin)”读取一个打开的文件fin,读取一个字符,然后返回一个int值。如果文件结束,fgetc将返回一个特殊标记EOF。从标准输入读取一个字符可以用getchar,它等价于fgetc(stdin)。
       不同操作系统的回车换行符是不一致的,在使用fgetc和getchar时,应该避免写出和操作系统相关的程序。
      2.  使用“fgets(buf, maxn, fin)”读取完整的一行,其中buf的声明为char buf[maxn]。这个函数读取不超过maxn-1个字符,然后在末尾添上结束符“\0”,因此不会出现越界的情况。一旦读到回车符“\n”,读取工作将会停止,而这个“\n”也会是buf字符串中最后一个有效字符(再往后就是字符串结束符“\0”了)。
表达式式"a?b:c"的含义是:当a为真时值为b,否则为c
常量数组并不需要指明大小,编译器可以完成计算。

#include<ctype.h>
if(isalpha(ch))//判断字符ch是否为字母,函数isalpha()包含在库ctype.h中

例题3-4 猜数字游戏的提示
实现一个经典"猜数字"游戏。给定答案序列和用户猜的序列,统计有多少数字位置正确(A),有多少数字在两个序列都出现过但位置不对(B)。
输入包含多组数据。每组输入第一行为序列长度n,第二行是答案序列,接下来是若干猜测序列。猜测序列全0时该组数据结束。n=0时输入结束。

#include <stdio.h>
#include <cstring>
#define maxn 100005
int ans[maxn];
int ges[maxn];
int A[maxn];
int B[maxn];
int main() {
   
    int n,game=0;
    memset(ans,0, sizeof(ans));//初始化数组
    memset(ges,0, sizeof(ges));
    memset(A,0,sizeof(A));
    memset(B,0,sizeof(B));
    while(scanf("%d",&n)&& n != 0){
    //当输入n==0时停止
        game++;
        int tag1[15] = {
   0},tag2[15] = {
   0};
        for(int i =0; i<n; i++){
   
            scanf("%d",&ans[i]);//输入答案
            tag1[ans[i]]++;//记录答案中每个数字出现的次数
        }
        int flag = 0,tot = 0;
        while(flag != n){
   //当输入全0时一组结束
            flag =0;
            for(int i =0; i<n; i++){
   
                scanf("%d",&ges[i]);
                tag2[ges[i]]++;//记录每次猜测中每个数字出现的次数
                if(ges[i]==0)
                    flag++;
            }
            for(int i =0; i<n; i++){
   
                if(ges[i]==ans[i])
                    A[tot]++;//记录每组猜测中位置正确的数量
            }
            for(int i =1; i<10; i++){
   
                B[tot] +=((tag1[i]>tag2[i])? tag2[i] :tag1[i]);
                //记录在两个序列都出现过但位置不对的数量
                tag2[i] = 0;//用完归零
            }
            B[tot] -= A[tot];
            tot++;
        }
        printf("Game %d:\n",game);
        for (int i = 0; i < tot-1; ++i) {
   
            printf("(%d,%d)\n",A[i],B[i]);//输出每组结果
        }
        memset(tag1,0, sizeof(tag1));//重置数组
        memset(A,0,sizeof(A));
        memset(B,0,sizeof(B));
    }
    return 0;
}
/*
 4
 1 3 5 5
 1 1 2 3
 4 3 3 5
 6 5 5 1
 6 1 3 5
 1 3 5 5
 0 0 0 0
10
1 2 2 2 4 5 6 6 6 9
1 2 3 4 5 6 7 8 9 1
1 1 2 2 3 3 4
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值