从 P1001 A+B 一路刷到 P5710,把所有踩过的坑和学到的知识点整理在这。
遇到不会的,先翻这份笔记。


目录


一、C 程序的基本骨架

1
2
3
4
5
6
7
8
#include <stdio.h>

int main()
{
// 你的代码

return 0;
}

要点

  • #include <stdio.h> 用 scanf / printf 必须有
  • main 是程序入口
  • return 0; 表示程序正常结束(不写也能过,但养成习惯)

二、输入输出 scanf / printf

核心规则

scanf 要地址(&),printf 要值(不用 &)。

1
2
3
int x;
scanf("%d", &x); // ✅ 要 &
printf("%d", x); // ✅ 不要 &

为什么?

  • scanf 要往变量里写数据,必须知道变量在哪 → 给地址(&x)
  • printf 只是读变量的值 → 给值(x)

漏掉 & 的后果:程序崩溃 → RE(踩过两次)

数组是例外

1
2
char s[100];
scanf("%s", s); // ⚠️ 数组名本身就是地址,不要加 &

三、数据类型与格式符对照表

⭐ 必背表

类型scanfprintf
int%d%d
long long%lld%lld
float%f%f
double%lf%f
char%c%c
字符串%s%s

⚠️ 最容易错的点

double 在 scanf 里用 %lf,在 printf 里用 %f!

原因:printf 会把 float 自动提升为 double,所以不用区分;scanf 拿到的是指针,必须区分。

输出控制格式

写法效果例子(值 3.14159)
%d整数—
%f默认 6 位小数3.141590
%.3f3 位小数3.142
%.1f1 位小数3.1
%g6 位有效数字3.14159
%.6g6 位有效数字3.14159
%02d宽度 2,不足补 008
%*c宽度用变量控制—

⚠️ 小数位数 vs 有效数字

1
2
%f  → 数的是"小数点后几位"    166.666667
%g → 数的是"从第一个非零数字起一共几位" 166.667

⚠️ %.1f 和 %.lf

1 是数字一,l 是小写字母 L —— 长得太像,极易看错。

⚠️ 尾行换行

题目输出最后一行通常不加 \n,多一个都可能 WA。(B2005 就栽在这)


四、变量与命名规范

命名原则

1
2
3
int total_jiao = a * 10 + b;   // ✅ 有意义
int x; // ⚠️ 太短
int ttl_Jiao; // ⚠️ 风格混搭

C 风格:全小写 + 下划线(total_jiao)或驼峰(totalJiao),选一种别混。

⚠️ 不要用这些名字

cout、cin、endl、string、max、min、count、time、index、y1

(都是 C++ 或标准库的名字,以后会冲突)

变量作用域

1
2
3
4
5
6
7
8
if (t == 1)
{
int apple = 10; // 只在这个 {} 里活着
}
else if (t == 2)
{
int apple = 20; // 另一个 apple,不冲突
}

{} 形成作用域,出了 {} 变量就消失了。


五、运算陷阱

⭐ 陷阱 1:整数除法(踩过 5 次以上!)

1
2
3
4
5
500 / 3        // = 166   整数除法,小数全丢
500.0 / 3 // = 166.6667 ✅

4 / 3 // = 1 ❌ 灾难
4.0 / 3.0 // = 1.333 ✅

规则:只要算式里有小数,参与运算的数就必须至少有一个是浮点类型。

三种修法

1
2
3
double a = (double)t / n;     // 强制类型转换
double a = t * 1.0 / n; // 乘 1.0
double a = 4.0 / 3.0 * pi; // 直接写浮点

⭐ 陷阱 2:向上取整 / 向下取整

场景方向写法
至少需要几个(不够也进位)向上(a + b - 1) / b
最多能买几个(不够就舍)向下a / b

例:走路 10 米,速度 3 米/分 → (10 + 3 - 1) / 3 = 4 分钟

⭐ 陷阱 3:浮点数不要用 == 比较

1
2
if (a == 0.1 + 0.2)     // ❌ 浮点几乎不可能精确相等
if (a > 0.2999) // ✅ 用 > 或 <

⭐ 陷阱 4:== 和 = 写错

1
2
if (t == 4)     // ✅ 比较
if (t = 4) // ❌ 赋值!条件永真,t 被覆盖

编译器可能只给警告,不报错。 防御写法:if (4 == t)(写错会立刻报错)

⭐ 陷阱 5:数据范围

范围提示用什么
保证答案在 int 范围内int
答案可能到 10^18long long
没说自己估算会不会溢出

int 上限约 21 亿(2.1 × 10⁹)

陷阱 6:类型不匹配导致输出乱码

1
printf("%d", a * 0.2 + b * 0.3);   // ❌ 算出来是 double,用 %d 打印 = 乱码

printf 完全信任格式符,类型错了它不知道,会读出垃圾。


六、逻辑运算符

运算符读作含义
&&与 AND两边都真才真
||或 OR有一个真就真
!非 NOT取反

⚠️ 别写成单个 & 或 |

单个是位运算,不是逻辑运算,写错了不报错但结果全错。

短路求值

1
if (i < n && a[i] > 0)    // 左边假 → 右边不看 → 防越界

关系表达式本身是 1 或 0

1
2
3
int p1 = (x % 2 == 0);       // p1 是 1 或 0
int p2 = (x > 4 && x <= 12);
int both = p1 && p2; // 可以直接当数用

“刚好一个成立” = 异或

1
int b = (p1 != p2);    // ✅ 一真一假

优先级

1
!  >  比较  >  &&  >  ||

不确定就加括号。


七、常见数学函数

1
#include <math.h>
函数作用例子
sqrt(x)平方根sqrt(117.0) → 10.8167
pow(x, y)x 的 y 次方pow(v, 1.0/3.0) 开立方
fabs(x)浮点绝对值fabs(-3.5) → 3.5
ceil(x)向上取整ceil(2.1) → 3.0
floor(x)向下取整floor(2.9) → 2.0
round(x)四舍五入round(2.5) → 3.0

⚠️ abs 和 fabs 不同

  • abs 在 stdlib.h,处理 int
  • fabs 在 math.h,处理 double

⚠️ 开立方用 pow

1
2
pow(v, 1.0 / 3.0)    // ✅ 1.0/3.0 必须是浮点!
pow(v, 1 / 3) // ❌ 1/3 = 0,结果变成 pow(v, 0) = 1

⚠️ 本地 gcc 编译要加 -lm

1
gcc a.c -o a -lm

洛谷上不用管。

⚠️ 浮点误差

1
2
(int)pow(v, 1.0/3.0)         // 可能是 15.9999998 → 得到 15 ❌
(int)(pow(v, 1.0/3.0) + 1e-9) // 加一点点容错 ✅

八、字符串基础

1
2
3
char s[100];              // 声明一个能装 100 个 char 的数组
scanf("%s", s); // ⚠️ 不加 &,数组名就是地址
int n = strlen(s); // 实际长度(需要 <string.h>)

⚠️ 容量 ≠ 长度

1
2
3
char s[100086];    // 容量 100086
scanf("%s", s); // 输入 "hello"
strlen(s) // 长度 5

字符串末尾有隐藏的 '\0',所以 char s[5] 装不下 5 个字母。

逐字符处理

1
2
3
4
for (int i = 0; i < n; i++)
{
printf("%c", s[i]);
}

倒着打印

1
2
3
4
for (int i = n - 1; i >= 0; i--)    // ⚠️ 是 n-1 不是 n
{
printf("%c", s[i]);
}

九、代码风格

缩进(Python 感的来源)

1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
if (a > b)
{
printf("大");
}
else
{
printf("小");
}

return 0;
}

规律

  • { 跟上一层对齐,或独占一行
  • 每深一层多 4 个空格
  • 用空格不用 Tab(网页 IDE 里 Tab 会错位)

空行分组 ⭐

用空行把"读入 / 计算 / 输出"分成块 —— 这才是看着舒服的关键。

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
int a, b;

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

int sum = a + b;

printf("%d", sum);

return 0;
}

空格规则

1
2
3
printf("%d\n", a + b);        // ✅ 逗号后加空格,前面不加
scanf("%d%d", &a, &b); // ✅
printf("%d\n" , a+b); // ❌ 逗号前有空格,运算符没空格

逗号/分号前面不加空格,后面加一个;运算符两边各一个空格。

危险运算要放在安全分支里 ⭐

1
2
3
4
5
6
7
8
if (t == 0)
{
printf("0");
}
else
{
int eaten = s / t; // ✅ 确认 t != 0 才敢除
}

你曾经把 s / t 写在 if 前面 → t = 0 时直接崩。


十、调试与排错

⭐ 打印调试法

在代码里临时加 printf 看中间变量:

1
2
3
printf("读到了: %s\n", s);
printf("长度: %d\n", strlen(s));
printf("中间结果: %d\n", eaten);

调完记得删掉。

⭐ 手工验算法

写完关键逻辑,用具体数字代进去念一遍:

1
2
apple / child      → "14 除以 4"  → 每人 3 个 ✔️
child / apple → "4 除以 14" → 0.28,取整 0 ❌ 变量放反了

这个方法能抓出 90% 的"变量放反"错误。

自测清单(交之前扫一遍)

检查说明
1. &scanf 里每个变量都有吗?
2. == vs =条件里写的是两个等号吗?
3. 拼写变量名前后一致?(大小写!lsat vs last)
4. 分号每行结尾都有吗?
5. 括号配对吗?数清楚了?
6. 格式符%d 配 int?%lf/%f 配对了吗?
7. 输入顺序和题目的变量顺序一致吗?
8. 尾行换行多加 \n 了吗?
9. return 0写了吗?

边界测试 ⭐

别只用样例测,想想极端情况:

  • t = 0?(除零)
  • 结果为负?(要不要归零)
  • 数据超范围?(溢出)
  • 空输入 / 单个元素?

十一、洛谷报错含义

缩写全称含义常见原因
ACAccepted通过—
WAWrong Answer答案错逻辑错、格式错、精度错
RERuntime Error运行崩溃漏 &、数组越界、除以 0
TLETime Limit Exceeded超时算法太慢
CECompile Error编译失败语法错误
MLEMemory Limit Exceeded内存超限数组开太大

⭐ 关键认知

OJ 报的是"最终状态",不是"bug 清单"。
一个 WA 背后可能藏着好几个错误,改完一个还有下一个。

所以修 bug 是循环:改 → 交 → 看结果 → 改 → 交。

看报错信息

CE 时编译器会告诉你:

1
error: 'child' undeclared; did you mean 'child3'?

直接就是答案。 越是新手越该认真读报错。

警告也要看:

1
warning: unused variable 'child3'

“定义了但没用” —— 常常正好指出你写错了变量名。


十二、刷题清单

【入门1】顺序结构(✅ 已完成)

题号题目状态
B2002Hello, World!⬜
B2025输出字符菱形⬜
P1000超级玛丽游戏⬜
P1001A+B Problem✅
B2005字符三角形✅
P5703苹果采购✅
P5704字母转换⬜
P5705数字反转✅
P5706再分肥宅水⬜
P5708三角形面积✅
P5707上学迟到✅
B2029大象喝水✅
P1425小鱼的游泳时间✅
P1421小玉买文具✅
P3954成绩✅

【入门2】分支结构(🔄 进行中)

序号题号题目状态
1P2433小学数学 N 合一✅
2P5709苹果和虫子✅
3P5710数的性质🔄 当前
4P5711闰年判断⬜
5P5712Apples⬜
6P5713洛谷团队系统⬜
7P5714肥胖问题⬜
8P5715三位数排序⬜
9P5716月份天数⬜
10P1085不高兴的津津⬜
11P1909买铅笔⬜
12P5717三角形分类⬜
13P1422小玉家的电费⬜
14P1424小鱼的航程⬜
15P1888三角函数⬜
16P1046陶陶摘苹果⬜
17P4414ABC⬜
18P1055ISBN 号码⬜

十三、踩过的坑速查

按踩坑顺序排列,每一个都是真实犯过的错。

#题目错误教训
1B2005输出多了尾行换行尾行 \n 会导致 WA
2P5703scanf("%d%d", x, y) 漏 &漏 & → RE
3P5705循环写成 i = 3,漏了最大下标下标是 0 到 len-1
4P5708s / S 大小写不一致C 大小写敏感
5P5708%.lf(字母 l)当成 %.1f(数字 1)这俩长得太像
6P1421int price = 19 漏分号交前扫一眼分号
7P3954%d 打印 double 表达式类型必须匹配格式符
8B2029scanf 又漏 &第二次踩同一个坑
9P2433if (t = 4) 少一个等号赋值 vs 比较
10P2433child 写成 child3 的错拼看编译器警告
11P24334 / 3 整数除法球体积少 1.33 倍
12P2433cout 输出精度 ≠ printf("%f")cout 相当于 %g
13P2433child / apple 变量放反用"念一遍"法自查
14P5709scanf 顺序写成 m s t(题目是 m t s)顺序必须对齐题面
15P5709s / t 写在 if (t == 0) 之前危险运算要放安全分支里
16P5709变量名打错 lsat警告"未使用变量"是线索
17P5709忘了"正在吃的苹果"也要减读题注意定语"完整的"

十四、常用套路总结

套路 1:统一单位

时间/钱/长度加减,先统一到最小单位,算完再换回来。

1
2
3
int start = a * 60 + b;      // 小时分 → 分钟
int total = end - start;
printf("%d %d", total / 60, total % 60);

好处:避免"借位"的麻烦。

套路 2:桶计数(数组当下标)

1
2
3
int ct[26] = {0};                // 计数数组,一定初始化
for (int i = 0; i < n; i++)
ct[s[i] - 'a']++; // 字符 → 下标

把 26 个 if 变成一行。

套路 3:字符 ↔ 数字

1
2
3
4
s[i] - 'a'      // 字符 → 下标
'a' + c // 下标 → 字符
c - 32 // 小写 → 大写(只对纯小写安全)
toupper(c) // 更安全的转换(需要 <ctype.h>)

套路 4:倒推

“经过一系列操作到达某个状态,问初始状态” → 从已知终点倒着推。

逆向运算:加变减、减变加、乘变除、除变乘,顺序也要倒过来。

例子(猴子吃桃):正向是 ÷2 - 1,逆向就是 +1 ×2。

套路 5:把数学式翻译成 C

老老实实按运算符优先级加括号:

1
2
// √(p(p-a)(p-b)(p-c))
double s = sqrt(p * (p - a) * (p - b) * (p - c));

⚠️ 根号罩住的范围,括号必须全部包住。


十五、下一步计划

  • [ ] 完成 P5710 数的性质
  • [ ] 刷完【入门2】剩余 16 题
  • [ ] 补做【入门1】漏掉的 B2002、B2025、P1000、P5704、P5706
  • [ ] 进【入门3】循环结构
  • [ ] 进【入门4】数组
  • [ ] 进【入门5】字符串
  • [ ] 进【入门6】函数与结构体

十六、还没学到但会遇到的东西

先混个眼熟,用到再学:

  • 数组:int a[100]; 存一批数据
  • 循环嵌套:两层 for 处理二维问题
  • 函数:把重复代码抽出来
  • 结构体:把相关数据打包
  • 排序:qsort 或手写
  • 数学算法:GCD(欧几里得)、质数筛、快速幂 —— 模板固定,背下来就行

⭐ 别提前焦虑,用到时再学最快。


笔记整理自洛谷【入门1】【入门2】刷题过程
最后更新:P5709 AC 之后