机试 C / C++ 注意事项

本文为《算法笔记》的学习记录

概念扫盲~

  • 在线评测系统 (Online Judge, OJ):
    一般来说, 在OJ上可以看到题目的题目描述、 输入格式、 输出格式、 样例输入及样例输出。如:PATcodeup
  • 评测结果
    AC:Accepted (答案正确)
    段错误:由数组越界,堆栈溢出(比如,递归调用层数太多)等情况引起

黑盒测试

黑盒测试是指: 系统后台会准备若干组输入数据,然后让提交的程序去运行这些数据,如果输出的结果与正确答案完全相同(字符串意义上的比较),那么就称通过了这道题的黑盒测试,否则会根据错误类型而返回不同的结果

单点测试 (PAT)

对单点测试来说, 系统会判断每组数据的输出结果是否正确。如果输出正确, 那么对该组数据来说就通过了测试, 并获得了这组数据的分值

多点测试 (codeup)

与单点测试相对,多点测试要求程序能一次运行所有数据, 并要求所有输出结果都必须完全正确,才能算作这题通过;而只要有其中一组数据的输出错误,本题就只能得0分

由于要求程序能运行所有数据, 因此必须保证程序有办法反复执行代码的核心部分,这就要用到循环。而题目一般会有3 种输入的格式,需要采用不同的输入方式。下面分别讲述这3 种输入方式

  1. while … EOF

如果题目没有给定输入的结束条件, 那么就默认读取到文件末尾

那么如何解决这种输入要求呢?首先需要知道,scanf 函数的返回值为其成功读入的参数的个数。在读取文件时到达文件末尾导致的无法读取现象, 就会产生读入失败,返回-1, 且C 语言中使用EOF 来代表-1。因此可以利用scanf 的返回值是否为EOF 来判断输入是否结束:

while (scanf("%d", &n) != EOF) {
    
    
	...
}

如果想要在黑框里面手动触发EOF, 可以按<Ctrl + Z>组合键

  1. while … break

题目要求当输入的数据满足某个条件时停止输入

扫描二维码关注公众号,回复: 12025050 查看本文章
while(scanf("%d%d", &a, &b) != EOF) {
    
    
	if (a == 0 && b == 0) break;
	printf ("%d\n", a + b);
}

而另一种更简洁的写法是,把退出条件的判断放到while 语句中,令其与scanf 用逗号隔开

while (scanf ("%d%d", &a, &b), a || b) {
    
    
	printf ("%d\n", a + b);
}
  1. while(T--)

在这种类型中,题目会给出测试数据的组数。由于给定了测试数据的组数,因此需要用一个变量T 来存储,并在程序开始时读入。在读入T 后,下面就可以进行T 次循环

最后需要指出,在多点测试中, 每一次循环都要重置一下变量和数组, 否则在下一组数据来临的时候变量和数组的状态就不是初始状态了

C / C++ 注意事项

  • s.c_str():返回string转化的字符数组,末尾自动加'\0'
  • 在给 long long 类型变量赋大于 2 31 − 1 2^{31} − 1 2311 的初值时,需要在初值后加上 LL。否则会编译错误
  • 单精度浮点数 float 的尾数有 23 位,因此有效精度只有 6~7 位 ( 2 23 = 8388608 2^{23}=8388608 223=8388608)。如果题目的精度要求较高,float就不太合适。因此,原则上浮点数全部用double
  • 小写字母比大写字母的 ASCII 码值大 32
  • 如果想定义常量,宏定义和const都可以,但推荐用const
  • 程序中无穷大的数可以表示为 (1 << 30) - 10x3fffffff
  • 循环 n n n 次的简洁写法:i = n; while(i--){}
  • 浮点数比较时,使用const double eps = 1e-8
    可以将比较操作宏定义为
    #define EQU(a,b) ((fabs((a)-(b)))<(eps))
    #define MORE(a,b) (((a)-(b))>(eps))
    #define LESS(a,b) (((a)-(b))<(-eps))
    #define MOREEQU(a,b) (((a)-(b))>(-eps))
    #define LESSEQU(a,b) (((a)-(b))<(eps))
  • const double PI = acos(-1.0);
  • 由于精度问题,在经过大量运算后,可能一个变量中存储的 0 是个很小的负数,这时如果对其开根号sqrt, 就会因不在定义域内而出错。同样的问题还出现在asin(x)x存放+1acos(x)x存放-1 时。这种情况需要用eps使变量保证在定义域内
  • 对时间复杂度为 O ( n 2 ) O(n^2) O(n2) 的算法来说, 当 n n n 的规模为 1000 1000 1000 时, 其运算次数大概为 1 0 6 10^6 106 级别;对一般的OJ系统来说, 一秒能承受的运算次数大概是 1 0 7   1 0 8 10^7 ~ 10^8 107 108 , 因此 O ( n 2 ) O(n^2) O(n2) 的算法当 n n n 的规模为1000时是可以承受的, 而当 n n n 的规模为100000时则是不可承受的
  • 在一般的应用中, 一般来说空间都是足够使用的(只要不开好几个 1 0 7 10^7 107 以上的数组即可,例如int A[10000][10000]的定义就是不合适的
  • 整数除以2四舍五入可以通过奇偶判断,以回避浮点数运算
  • 如果用字符数组的话,可以引用头文件<cstring>来使用strlen等函数
  • 如果想读入一行字符串到字符数组,则需要使用fgets(c, n, stdin)(表示从标准输入读入n个字符到字符数组c中),要注意fgets是会读入末尾的换行符然后在换行符后面加’\0’的,同时在使用fgets之前可能需要用getchar来读入多余的换行符,否则fgets会只读入换行符就结束了

scanf / printf

  • 输入和输出请使用 scanfprintf !(#include <cstdio>) (它们比 cincout 要快得多。并且不要混用它们和 cincout,有时会出问题

如果要输入"3 4" 这种用空格隔开的两个数字, 两个%d 之间可以不加空格:

int a, b;
scanf("%d%d", &a, &b);

可以不加空格的原因是,除了%c外,scanf对其他格式符(如%d)的输入是以空白符(即空格、Tab) 为结束判断标志的, 因此除非使用%c把空格按字符读入, 其他情况都会自动跳过空格。另外, 字符数组使用%s读入的时候以空格跟换行为读入结束的标志, 如下面的代码所示:

char str[10];
scanf("%s", str);
printf("%s", str);

输入数据:

abed efg

输出结果:

abed

在这里插入图片描述
由表2-6 可见,double 的输出格式变成了%f, 而在scanf 中却是%lf。在有些系统中如果把输出格式写成%lf 倒也不会出错,不过尽量还是按标准来

如果想要输出’%’’\’, 则需要在前面再加一个%\, 例如:

printf("%%");
printf("\\");
  • %0md:使不足m 位的int 型变量以m 位进行右对齐输出,其中高位用0补齐;如果变量本身超过m 位, 则保持原样

getchar / putchar
getchar可以读入换行符

sscanf / sprintf

scanfprintf 中的 标准输入 / 输出流 换成了字符数组

sscanf(str, "%d", &n);
sprintf(str, "%d", n);
int n;
char str [100] = "123";
sscanf(str, "%d", &n);
printf("%d\n", n);

输出结果:

123
int n = 233;
char str[100];
sprintf(str, "%d", n);
printf("%s\n", str);

输出结果:

233

sscanf 还支持正则表达式, 如果配合正则表达式来进行字符串的处理, 那么很多字符串的题目都将迎刃而解

常用 math 函数

#include <cmath>
  • fabs(double x):取绝对值
  • floor(double x)ceil(double x):分别用于向下和向上取整
  • pow(double r, double p):返回 r p r^p rp
  • sqrt(double x)
  • log(double x):返回 l o g e x log_ex logex ( l o g a b = log ⁡ e b l o g e a log_ab=\frac{\log_eb}{log_ea} logab=logealogeb)
  • sin(double x)cos(double x)tan(double x): 参数要求是弧度制
  • asin(double x)acos(double x)atan(double x): 这三个函数分别返回double 型变量的反正弦值、反余弦值和反正切值 (pi=acos(-1.0))
  • round(double x):四舍五入

数组初始化

memset

#include <cstring>
memset(数组名,, sizeof(数组名))
memset(数组名,, 数组大小 * sizeof(数组元素类型))

只建议使用memset0-1。这是因为memset使用的是按字节赋值, 即对每个字节赋同样的值, 这样组成int型的4个字节就会被赋成相同的值。而0的二进制补码为全0, -1的二进制补码为全1, 可以直接将数组内所有元素赋值为0或-1

如果要对数组赋其他数字(例如1), 那么请使用fill函数(但memset的执行速度快)

常用的泛型函数

#include <algorithm>

sort()

sort 就是用来排序的函数, 默认使用<进行升序排序。它根据具体情形使用不同的排序方法, 效率较高

vector<int> v{
    
    3, 2, 1};
sort(v.begin(), v.end()); // 使用迭代器

int a[] = {
    
    3, 2, 1};
sort(begin(a), end(a)); // 需要 #include <iterator>
// 或者
sort(a, a + sizeof(a) / sizeof(int));

可以自定义一个二元谓词作为比较函数,作为sort的第三个实参

// 比较函数, 用来按长度排序单词
bool isShorter(const string &s1, const string &s2)
{
    
    
	return s1.size() < s2.size();
}
// 按长度由短至长排序 words
sort(words.begin(), words.end(), isShorter);

// 比较函数也可以用lambda 来更方便的实现
sort(words.begin(), words.end(),
			[] (const string &a, const string &b)
				{
    
     return a.size() < b.size(); });

如果想要降序排序,最简单的方法是使用反向迭代器

sort(vec.rbegin(), vec.rend());

stable_sort

用法与sort相同,但是是稳定的排序方法

猜你喜欢

转载自blog.csdn.net/weixin_42437114/article/details/108816378