C 语言程序设计(程序设计竞赛方向)快速入门指南

纸上得来终觉浅,绝知此事要躬行。

本指南适用于零基础但有一定自学能力且对程序设计竞赛有兴趣,希望通过突击学习 C 语言程序设计在软件学院 AK 杯程序设计竞赛获奖并进入香农先修班学习的同学。由于是入门指南,为了尽可能地保证循序渐进,本文适当地做了知识屏蔽处理。

20200919 更新: 这篇文章本质上还是 C 语言的学习指引哦!如果你希望参与到程序设计竞赛,主要使用的还是 C++ 哦!C++ 是 C 语言的超集,如果你有 C++ 程序设计基础那当然是最好不过的啦!AK 杯程序设计竞赛我们也非常欢迎大家使用 C++ 交题哦!

Step 1. C 语言语法快速学习

废话不多说,我们直接开始吧。下面这个慕课覆盖了学校十到十一月 C 语言程序设计的授课内容,也覆盖了 AK 杯程序设计竞赛中需要的所有语法知识点。尝试快速阅读慕课对每一个知识点的图文版讲解并完成配套的编程题。

https://www.imooc.com/learn/249

具体操作指引

imooc.com 注册一个账户,加入上述的慕课课程,按照课程顺序完成每一个知识点的学习。

如图,课程页面分为两个板块。左侧的板块为图文版的知识点讲解,右侧的板块为在线编辑器。

请先阅读并理解左侧的知识点介绍,知识点介绍的后面是一道程序设计题,通常是要求你对在线编辑器中的代码进行补全和修改。根据要求在在线编辑器中完成练习,点击「提交」。系统会自动判断你的程序能否运行且能否输出需要输出的内容。程序的运行结果和系统的判题结果都会显示在页面最右边的空白处。如果显示通过,就可以进入下一节的学习,否则还要继续修正自己的程序哦!

如果你在完成练习时遇到困难,尝试重新阅读知识点并在必要时进行百度搜索(我们论坛已经和百度达成了合作关系,在百度上搜索可以节省你很多的时间哦)。实在没有思路可以点击「不会了怎么办」参考慕课给出的思路与答案。

我们建议你在国庆结束之前完成这一步。但愿你在暑假已经对 C 语言有了初步的了解,不然在军训期间学习 C 语言可能确实是一个艰巨的任务,或许你确实需要摆出钢铁一般的意志。不管怎么说,加油啦。

Step 2. 本地开发环境配置

好耶,我们已经掌握了一些基本的 C 语言语法,也完成了一些程序设计题。但这一切好像都是在慕课上做的,那结束了慕课的学习之后我们要上哪去写代码呢?或许你会想到记事本和 Word。但是还有一个问题,就是慕课的代码编辑器有一个神奇的「提交」按钮,点下这个按钮就可以一键对答案。这就好比你高中刷题,刷完题总得对一下答案。我们在记事本和 Word 上好像没有这个功能耶。

还是换一个地方写代码吧!事实上,我们在本地进行 C 语言程序设计使用的是一个叫做 Code::Blocks 的集成开发环境。我们不但可以在 Code::Blocks 上写代码,还可以点击一个叫做「编译+运行」的按钮,写好的代码立马就跑起来啦,效果和慕课上的「提交」按钮一样的哦!我们赶紧用起来吧。

具体操作指引

我们先把 Code::Blocks 下载下来吧。这里有最新版的下载链接,点开等上几秒就会自动开始下载的啦。

接下来我们跟着下面的教程,把 Code::Blocks 安装配置好。教程中演示的是 16.01 版本,而上面的链接是 20.03 版本,但整体流程应该大同小异。如果遇到问题,别忘了读一下 Code::Blocks 给出的报错信息,此外别忘了我们论坛和百度已经达成了合作关系,可以去百度搜索哦。

能否成功地运行经典的 Hello World 程序呢?

#include <stdio.h>
int main()
{
    printf("Hello World");
    return 0;
}

如果可以的话一切就准备就绪啦,闲下来的时候尝试在 Code::Blocks 上面敲上几行,点击无敌的「编译+运行」按钮。如果黑框框弹了出来,显示的正是你希望程序输出的内容,是不是成就感满满?如果没有黑框框弹出来,报错信息都会在 Code::Blocks 底部显示出来哦,尝试根据上面的信息修改你的代码吧。

Step 3. 程序设计之输入输出专题

接下来我们要进一步学习 C 语言啦!在前面我们已经了解到 printf() 函数,这是一个用于输出数据的函数。接下来我们要另外学习一个函数 scanf(),这个函数和 printf() 相反,是从键盘输入中读取数据。虽然两个函数的用途不一样,但用法其实差不多,在程序设计竞赛我们会经常使用到这两个函数哦,我们来看看吧!

我们输出一个 int 变量 a 的值用的是:

printf("%d", a);

那么我们怎么从键盘输入中读取一个整型数,并保存到 int 变量 a 呢?

scanf("%d", &a);

没错,我们依然使用到了格式控制符(也就是上面的 %d),然而你会发现除了将 printf 替换成了 scanf 之外,变量的前面多了一个 & 号。在几乎所有情形下,如果你希望使用 scanf() 从键盘读入什么并保存到变量里,都别忘了在变量前面加上 & 号。唯一的例外是当你需要从键盘输入中读入字符串的时候,这时 a 的前面不能加上 & 号:

#include <stdio.h>
int main()
{
    char a[1000];
    scanf("%s", a); // 从键盘输入中读入一个字符串并保存到数组 a。
    printf("%s\n", a);
    return 0;
}

当然还有更多的用于输入和输出的函数等待我们去了解,当然在 AK 杯熟练掌握 scanf()printf() 就足够啦!由于输入输出真的可以很坑人,我们在香农先修班中还会进一步讲解输入输出的啦,AK 杯也不会在这一方面为难大家的(今年的 GMCPC 网络赛 A 题的读入可把笔者给坑惨啦)。

https://www.runoob.com/cprogramming/c-input-output.html

具体操作指引

首先,让我们在 Code::Blocks 编写下面的程序:

#include <stdio.h>
int main()
{
    int a, b;
    scanf("%d", &a);          // 从键盘输入中读入一个整数并保存到变量 a。
    scanf("%d", &b);          // 从键盘输入中读入一个整数并保存到变量 b。
    printf("%d\n%d\n", a, b); // 打印 a 和 b 的值。
    return 0;
}

点击「编译+运行」,会弹出一个黑框框,在黑框框里面输入两个整数。具体怎么输入呢,就是敲一个整数,敲回车或空格,再敲一个整数,敲回车。我们用空格和回车分隔要输入的数字,而不用其它的字符(划重点啦)。现在观察输出的结果是不是你输入的两个整数呢?这也就是最简单的数据读入啦!

接下来编写下面的程序:

#include <stdio.h>
int main()
{
    int a, b;
    scanf("%d%d", &a, &b);   // 从键盘输入中读入两个整数并保存到变量 a 和变量 b。
    printf("%d %d\n", a, b); // 打印 a 和 b 的值。
    printf("%d%d\n", a, b);  // 又一次打印 a 和 b 的值。
    return 0;
}

点击「编译+运行」,依然按照前面的格式输入两个整型数,看一下这回的输出是否符合你的预期呢?你会发现这回我们在同一个 scanf() 一次性读入了两个整型数,而两个格式控制符中间没有加任何东西。正如前面所说,这是因为我们在键盘输入的时候,会使用空格和回车分隔了这两个数,只要我们按着前面提到的格式敲,格式控制符中就不需要加任何东西。但是,输出是另外一回事哦!观察一下 printf() 打印出来的东西,少了空格两个数就黏在一起啦,千万不要搞混淆哦!

接下来我们引入更加多样的输入。现在你需要从键盘输入中依次读入一个长长整型数 a 、一个单精度浮点数 b 、一个长度不超过 100 且不包含空格的字符串 c ,然后你要将它们重新打印出来,另外还希望你打印字符串 c 的第一个字符:

#include <stdio.h>
int main()
{
    long long a;
    float b;
    char c[110];
    scanf("%lld%f%s", &a, &b, c); // 读入 a, b, c。
    printf("%lld\n%f\n", a, b);   // 打印 a 和 b。
    printf("%s\n%c\n", c, c[0]);  // 打印 c 之后再单独打印 c 的第一个字符。
    return 0;
}

你看,格式控制符还是那些格式控制符,但别忘了在读入字符串的时候不需要加 & 号哦!现在我们再次点击「编译+运行」按钮。这回我们要怎么在黑框框中输入东西呢?还是一样,输入一个长长整型数,输入空格或回车,输入一个浮点数,输入空格或回车,输入一个字符串。输出的前三行数据,正是我们刚刚读入的内容。而第四行正是字符串 c 的第一个字符,之所以提及这个是希望大家不要忘记在 C 语言中字符串保存到的是字符数组,那么是数组的一些用法也是能用在字符串上的哦!

先介绍这么多啦,足够应付大多数的情况啦!玄学读入的处理我们在香农先修班中会介绍哦。

Step 4. 程序设计竞赛入门

来到最后一步啦!有了 C 语言程序设计基础,就可以入门程序设计竞赛啦。程序设计竞赛决不仅仅是考察你会不会写循环语句分支语句会不会用数组,更会考察一个人对离散数学、数据结构与算法的掌握程度。当然 AK 杯主要还是看你 C 语言掌握的如何以及思维是否灵活啦。

那么在 AK 杯之前要做些什么呢?当然可以提前学习算法知识,但其实直接做题就可以啦。在线判题平台(简称 OJ)提供了大量的题目,洛谷(luogu.com.cn)就是其中一个 OJ,上面不少的题目对初学者非常友好,特别是标有「入门」的题目。所以接下来你要做的,就是软协宣讲会提到的 20 道「入门」、5 道「普及-」和 1 道「普及/提高+」,加油啦!

具体操作指引

怎么在洛谷上做题呢?跟在慕课上做题其实是有一点不同的,当然 注册帐号 还是要的啦!

然后我们来到 题目列表,选定一道题目点开,阅读上面的题面和输入输出要求。

接下来,我们在 Code::Blocks 上完成程序的实现,并利用「编译+运行」测试我们的代码(可以将样例输入复制粘贴进黑框框里面,看看输出的是不是样例中的结果)。然后我们点击「提交答案」:

接下来我们把写好的程序粘贴上去,点击「提交评测」:

为什么不像慕课一样,在上面的在线编辑器直接完成编程呢?其实确实的可以的,但是 Code::Blocks 提供的代码补全功能确实好用,我们也就提倡大家在 Code::Blocks 上完成程序设计啦!更重要的是,在正式的程序设计比赛中,如果你将你的代码提交评测之后评测不通过,会给你一定的惩罚,直接的结果就是排名下降。所以还是要在本地先「编译+运行」测试一下我们的代码,然后再自信满满地提交评测啦。

接下来我们就一起来完成最经典的 A+B Problem 吧!

https://www.luogu.com.cn/problem/P1001

我们来看一下题目,输入两个整数 ab ,输出它们的和。数据范围是 |a|,|b| \leq 10^9 。输入格式是两个整数以空格分开,输出格式是一个整数。输入和输出格式到底是什么意思呢?我们就得说说 OJ 是怎么评测我们的代码的啦。事实上在提交代码之后,OJ 并不会捉一个可怜的工具人出来,真正地去阅读你的代码,找出代码中的错误;而是有着一套全自动的机制。

具体而言评测机将你的代码「编译」成可执行文件,然后反复「运行」这个文件,每一次运行的时候会严格按照输入格式输入一组数据,我们称之为一个测试点。因为题目限制了你的输出格式,那么评测机就可以直接将你的输出结果和答案进行比对。如果是完全一致的,那么这个测试点就评测通过了。如果所有测试点的评测通过了,那么你的代码在这道题目也就评测通过啦。

所以说我们一定要按照输入格式依次读入数据,然后按照输出格式打印我们的答案哦!

那我们打开我们的 Code::Blocks,开始我们的程序设计吧!首先,头文件和主函数自然是要写好的啦:

#include <stdio.h>
int main()
{
    return 0;
}

接下来,我们要把读入数据读进来保存到变量里面。虽然样例输入给定的两个数是 2030 ,但实际评测的时候评测机输入的并不一定是这两个数哦!但是评测机输入的数一定满足下面的条件: |a|,|b| \leq 10^9ab 是整数,两个整数以空格分开。那我们就放心啦,我们声明两个变量 ab ,然后使用 scanf() 函数。现在我们的程序是这样的:

#include <stdio.h>
int main()
{
    int a, b;
    scanf("%d%d", &a, &b);
    return 0;
}

好啦,现在我们要打印 a+b 的值。我们就使用 printf() 函数输出这个答案吧!

#include <stdio.h>
int main()
{
    int a, b;
    scanf("%d%d", &a, &b);
    printf("%d", a + b);
    return 0;
}

当然了这里的输出格式要求很简单,只需要打印一个整数。我们也就打印一个整数,也不去添加其它东西。但是有些输出格式要求也是在为难人,要求打印的是多行的数据,那么别忘记在合适的位置打印换行符 \n 哦!当然啦,评测机其实很聪明,在比对答案的时候,会忽略整个输出最末尾的换行符,也就是说所以在做 A+B Problem 的时候,我们可以也这样输出答案:

printf("%d\n", a + b);

好啦,我们的程序设计就完成啦!先不要急着将代码提交到洛谷,我们在 Code::Blocks 测试一下我们的代码。点击「编译+运行」,如果你的代码没有语法错误,黑框框就会弹出来。我们现在使用样例测试一下我们的程序,让我们在黑框框里面输入:

20 30

具体而言,就是敲一个 20 ,敲一个空格,再敲一个 30 ,再敲一个回车表示我们输入完了。你会发现,程序马上打印了一个数 50 并结束了运行。 50 这个结果和样例输出无论是内容还是格式都是一样的。太棒了,看来我们很有希望通过洛谷的系统评测。

现在我们按照前面所说,前往提交页面 将我们在 Code::Blocks 完成的代码复制粘贴过去。点击「提交评测」,让我们期待一下,看看洛谷给出的评测结果会是如何吧!

我们可以看到页面右栏的评测状态写有 Accepted,那么恭喜你,代码评测通过啦!如果写有的是 Unaccepted,那么很遗憾,评测没有通过,还需要继续修改你的程序哦!页面左栏则是具体到每一个测试点的评测结果啦,这里的 AC 是 Accepted 的简写形式,这里有一些常见的测试点评测结果,我们来了解一下吧:

评测状态 含义
Accepted AC 恭喜!你的代码成功通过地了这个测试点评测。
Compile Error CE 编译出错,也就是你的程序中有语法错误。不妨回想一下,你是否还没在 Code::Blocks 测试过你的代码就直接在 OJ 上提交了呢?
Wrong Answer WA 答案错误,也就是在这个测试点评测中,你的程序输出的结果和答案不一致。可能是代码的逻辑有问题,或者是某一个细节没有考虑到,再仔细想想吧!
Time Limit Exceeded TLE 运行超时,也就是在这个测试点评测中,你的程序在规定的时间内结束运行。这不是评测机卡顿造成的哦,想想你的代码中是否引入了死循环,再尝试对你的代码进行进一步的优化吧!
Runtime Error RE 运行时错误,也就是在这个测试点评测中,你的程序无法正常结束运行。这不是评测机故障造成的哦,只要你的代码中除零错误、数组越界访问、未定义行为等都有可能导致运行时错误。再仔细检查一下你的程序吧!

当然还有 MLE 和 OLE 等等评测结果,这篇文档给出了非常详细的说明,可以去看看哦!

我们来试一下吧,不妨在这个题目提交下面的代码:

#include <stdio.h>
int main()
{
    int a, b;
    scanf("%d%d", &a, &b);
    printf("%d", a - b);
    return 0;
}

你可能已经发现了,这不是在算 a-b 的值吗,这不就违背了题目的意思吗?我们先来看看评测结果:

果然,所有的测试点评测结果都变成了 WA,也就是答案错误。我们再来试一下提交下面的代码:

#include <stdio.h>
int main()
{
    int a, b;
    scanf("%d%d", &a, &b);
    while(1);
    printf("%d", a - b);
    return 0;
}

你可能又会吐槽了,while(1); 这不就让程序陷入死循环了吗?我们再来看看评测结果:

看,因为程序没有在规定时间内结束运行,所有的测试点评测结果都变成了 TLE,并不是评测机的问题吧!对于 Unaccepted 的结果,洛谷会提供第一个没有通过的测试点数据,但是尽量不要去下载哦!这是因为正式比赛是不会有这么多花里胡哨的东西提供给你的哦,还是要自己想办法修正自己的程序啦。

好了,现在相信你也大致了解使用洛谷做题了。那么接下的问题是,怎样从浩如烟海的题库中找到适合自己的 20 道「入门」、5 道「普及-」和 1 道「普及/提高+」呢?洛谷提供了一个叫做「题单」的功能:

https://www.luogu.com.cn/training/list

让我们来看看前几个题单的名字:顺序结构、分支结构、循环结构… 这不就正好对应上之前的 C 语言语法学习中学到的内容吗?太棒啦,事实上前六个题单正是为巩固 C 语言而准备的题单,上面的题目都不会涉及到离散数学和数据结构的知识,可以大胆尝试哦,完成这几份题单之后 20 道「入门」、5 道「普及-」和 1 道「普及/提高+」的任务也就自然完成了,那就可以发截图给我啦!AK 杯题目的难度和题单中题目的难度是差不多的啦!那就祝大家好运啦!有关 AK 杯的具体说明会另外开帖说明的哦,大家就期待一下吧!


后记:第二版终于写完啦!写着写着就开始啰哩啰唆,也不知道大家有没有读完,打不打算按着上面的步骤走。事实上,笔者就是这样走过来的,AK 杯也没有 AK,混进集训队之后也挺颓废的(捂脸)。不过最近也开始想如何给你们出题啦!听 lxy 说要把小学生队拉过来打内心还是很慌乱的。还是希望大家可以打过小学生,给我们学院争光(继续捂脸)。最后的最后,如果不是以 AK 杯为目标去学习 C 语言的话笔者推荐的还是 Bintou 老师在 0xffff 提到的 How to Think Like a Computer Scientist: C Version,毕竟程序设计竞赛也只是一个选择而已,还是要看自己的兴趣啦,确实没有兴趣的,也不要勉强自己啦,会很痛苦的。

5赞

本论坛已经和xx、xxx、xxxxxx、xxxxxxxx、xxxxxxxx……达成合作可还行 :face_with_thermometer:

AK杯有防AK题系列

1赞

什么,竟然有小学生大佬来打比赛?

粤 ICP 备 2020080455 号