考试常用语法点整理合集

· · 个人记录

本文是笔者 2023 CSP-S 复习写的博客,主要是总结知识点用,如有遗漏或不准确,还请海涵。

\

Part1 输入与输出

\

1.1 cin /coutscanf/ printf

C++ 标准的输入输出是 cincout,它们可以输入/输出绝大部分常用的数据类型,以下给出一些常见数据类型。

整型:int / long long / unsigned int / unsigned long long

int a;
long long b;
unsigned int c;
unsigned long long d;
cin >> a >> b >> c >> d;
cout << a << b << c >> d;

浮点型:float / double

float a;
double b;
cin >> a >> b;
cout << a << b;

字符/字符串型:char / char* / string

char a;
char b[maxn];
string c;
cin >> a >> b+1 >> c;
cout << a << b+1 << c;
\

但是,有些题目对于输入/输出有格式上的要求,而 cincout 在格式上很难操控,因此我们常用 scanfprintf 来代替。

整型:int / long long / unsigned int / unsigned long long

int a;
long long b;
unsigned int c;
unsigned long long d;
scanf("%d %lld %u %llu", &a, &b, &c, &d);
printf("%d %lld %u %llu", a, b, c, d);

浮点型:float / double

float a;
double b;
scanf("%f %lf", &a, &b);
printf("%f %lf", &a, &b);

字符/字符串型:**char / char***

char a;
char b[maxn];
scanf("%c %s", &a, b+1);
printf("%c %s", a, b+1);
\ \

对于输入/输出 8 进制或 16 进制的数字,scanfprintf 的操作非常简便。

int a;
scanf("%o", &a);
printf("%o", a);
//%o 是八进制 (o 即为 octal 八进制的缩写)
scanf("%x", &a);
printf("%x", a);
//%x 是十六进制 (x 即为 hex 八进制的缩写)
//当然,%d 是十进制 (d 即为 decimal 八进制的缩写)
\

比如有一个数字 114514,如果只想读入前 4 位:

int a;
scanf("%4d", &a);
\

比如要输入 2023-9-25 22:39:10 这个时间,那么可以用 scanf 很方便地读入。

int year, month, day, hour, minute, second;
scanf("%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);

当然,这样限定长度也是可以的。

比如输入 11 45 14,但我只想要两边的数字,不要中间的 45,可以这么写:

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

这样 a = 11b = 14

\

比如 114.514,我只想保留一位小数:

double a = 114.514;
printf("%.1lf", a);

注意四舍五入。

\

1.2 换行

在输出一行文本时,常用的换行符有两种。

cout << a << endl;
printf("%d\n", a);

但是实际上,cout 也是可以使用 '\n' 的。

cout << a << '\n';

注意这里是单引号,也就是说 '\n' 整体是一个字符

\

在这里阐述一下我只使用 '\n' 而从来不用 endl 的原因:因为 endl'\n' 更慢,据说是要刷新一些东西(这个不是很懂),而且我主要使用的是 scanfprintf,所以 endl 很少碰。

在一些输入输出数据非常大的题目中,如果你使用 endl 可能会喜提 TLE,但是用 '\n' 就能 AC。

还有一些输出换行符的方式:

putchar('\n');

实际上就是单独输出一个 '\n' 字符嘛。

\
puts("");

虽说写起来比较短,但是尽量我很少使用。

\

1.3 读入字符

单独把读入字符列出来的原因是,scanf("%c", &c) 这玩意儿会读入空格,有的时候会很麻烦。

怎么解决?

cin读入字符串即可。

scanf("%s", s+1);
\

用这个东西代替你读入单个字符,然后你要的那个字符就是 s[1]

因为读入字符串会自动过滤空格,所以这样是没问题的。

\

当然,狠人可以使用 getchar (也就是和 putchar 对应的,输入/输出单个字符),然后不断过滤空格。

char c = getchar();
while(c == ' ' || c == '\n') c = getchar();
\

1.4 读入整行

也就是说,一行中有若干个字符串(之间有空格),现在要把它们全部输入进一个字符串里。

当然,scanf 是可以做到这个操作的,但是这里也可以使用 cin

形式类似于这样:

string s;
getline(cin, s);

就可以了。

\

1.5 IO优化

IO 优化,也就是针对所谓的输入/输出效率优化,这里介绍三种常用方法。

\

第一种 关闭同步

这个主要针对 cincout 来使用,它们的代码长这样:

ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);

牢记这三行。

这个优化可以让 cincout 的效率和 scanfprintf 接近(虽说还是差了一丁点)。

但是有一个大忌:由于关闭了同步,在写了上面的三行代码之后,绝对不要使用任何 scanfprintf

如果你使用了,这样做的后果就是输出紊乱

切记,切记!

\

第二种 使用 scanfprintf

既然关闭同步的效率和 scanfprintf 差不多,那我为什么不直接使用 scanfprintf 呢()。

\

第三种 手写输入输出

这个是三种方法中速度最快的方法了。当然,这么快的代价就是要写很长一段,不过比赛前十分钟,你应该可以抽出空写。

这里给出输入/输出 int 的快读模板:

int read() {
    int res = 0, flag = 1; char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') flag = -1; c = getchar(); }
    while(c >= '0' && c <= '9') { res = res*10 + c-'0'; c = getchar(); }
    return res*flag;
}

void print(int x) {
    if(x > 9) print(x/10);
    putchar(x%10 + '0');
}
\

1.6 一些例外

\ \

Part2 数组及相关内容

\

1.1 定义

数组,也就是一组数,用来开辟若干大小的空间,开辟若干个变量。

正常数组的定义方式有三种。

\ \ \

1.2 遍历

一般使用 for 循环遍历所有元素。当然,不遍历所有元素也是可以的。

for(int i = 1; i <= n; i++) {
    a[i] += 2;
    //程序内容
}

二维数组也是一样。

for(int i = 1; i <= n; i++)
    for(int j = 1; j <= m; j++) {

    }
\

1.3 传参

对于一个数组 a[n]a 代表的是这个数组的首地址,也就是第一个元素的地址。

第一种

void f(int* a) {
    //程序内容
}

第二种

void f(int a[]) {
    //程序内容
}
\

第一种

for(int i = 1; i <= n; i++)
    scanf("%d", &a[i]);

第二种

for(int i = 1; i <= n; i++)
    scanf("%d", a+i);

注意下面是 a+i,因为 a 是地址,不需要再写取地址符 &

\ \

1.4 初始化/覆盖

假如有一个数组 a,想要把里面都清空成 0,怎么做?

\ \

那如果都清空成 1 呢?

请注意,此时不能使用 memset,只能使用 for 循环。

for(int i = 1; i <= n; i++)
    a[i] = 1;
\

不难发现,memset 虽然好用,但是是有局限性的。

一般来讲,memset 里可以传入的数字有 0-1。这些修改都是成功的。

比较特殊地,如果要将数组都变成一个很大的数字,一般来说是 0x3f3f3f3f,那么也可以这么写:

memset(a, 0x3f, sizeof(a));

如果要将数组都变成一个很大的数字,那么也可以这么写:

memset(a, 0xc0, sizeof(a));
\

当然,在数组第一次使用时,只要你声明的是全局变量,那么不清零也是可以的,因为默认是 0

\

1.5 常用函数

排序函数

比较常见的就是将数字元素排序。

在默认情况下,sort 函数是从小到大排序的。

sort(a+1, a+1+n);

请注意,这里面 a+1 是第一个元素的指针,a+1+n 是最后一个元素的后一个指针。(也就是说,a+1+n 这个地址是没有数字的。)

\

至于从大到小排序,以及其它更复杂的排序,我会在后面的内容讲到。

\
去重函数

也就是 unique 函数,它和 sort 函数的参数一样。

但是,它的返回值是去重后最后一个元素的后一个指针,因此如果想记录数组中不同元素的个数,可以这么写:

sort(a+1, a+1+n);
//不要忘记,使用 unique 函数之前,一定要保证数组已经排好序
int m = unique(a+1, a+1+n) - a - 1;

牢记。

\
二分查找

手写的二分查找当然没问题,不过这里主要说的是 lower_boundupper_bound

它们的写法和 sortunique 也是一样的,同样也返回的是指针。

在确定一个元素的位置,常见写法是这样的:

int pos = lower_bound(a+1, a+1+n, x) - a;
\

先总结到这,如有遗漏,欢迎提出!

To \ be \ continued...