C 语言洛谷刷题笔记
从
P1001 A+B一路刷到P5710,把所有踩过的坑和学到的知识点整理在这。
遇到不会的,先翻这份笔记。
目录
- 一、C 程序的基本骨架
- 二、输入输出 scanf / printf
- 三、数据类型与格式符对照表
- 四、变量与命名规范
- 五、运算陷阱
- 六、逻辑运算符
- 七、常见数学函数
- 八、字符串基础
- 九、代码风格
- 十、调试与排错
- 十一、洛谷报错含义
- 十二、刷题清单
- 十三、踩过的坑速查
一、C 程序的基本骨架
1 |
|
要点
#include <stdio.h>用scanf/printf必须有main是程序入口return 0;表示程序正常结束(不写也能过,但养成习惯)
二、输入输出 scanf / printf
核心规则
scanf要地址(&),printf要值(不用&)。
1 | int x; |
为什么?
scanf要往变量里写数据,必须知道变量在哪 → 给地址(&x)printf只是读变量的值 → 给值(x)
漏掉 & 的后果:程序崩溃 → RE(踩过两次)
数组是例外
1 | char s[100]; |
三、数据类型与格式符对照表
⭐ 必背表
| 类型 | scanf | printf |
|---|---|---|
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 |
%.3f | 3 位小数 | 3.142 |
%.1f | 1 位小数 | 3.1 |
%g | 6 位有效数字 | 3.14159 |
%.6g | 6 位有效数字 | 3.14159 |
%02d | 宽度 2,不足补 0 | 08 |
%*c | 宽度用变量控制 | — |
⚠️ 小数位数 vs 有效数字
1 | %f → 数的是"小数点后几位" 166.666667 |
⚠️ %.1f 和 %.lf
1 是数字一,l 是小写字母 L —— 长得太像,极易看错。
⚠️ 尾行换行
题目输出最后一行通常不加 \n,多一个都可能 WA。(B2005 就栽在这)
四、变量与命名规范
命名原则
1 | int total_jiao = a * 10 + b; // ✅ 有意义 |
C 风格:全小写 + 下划线(total_jiao)或驼峰(totalJiao),选一种别混。
⚠️ 不要用这些名字
cout、cin、endl、string、max、min、count、time、index、y1
(都是 C++ 或标准库的名字,以后会冲突)
变量作用域
1 | if (t == 1) |
{} 形成作用域,出了 {} 变量就消失了。
五、运算陷阱
⭐ 陷阱 1:整数除法(踩过 5 次以上!)
1 | 500 / 3 // = 166 整数除法,小数全丢 |
规则:只要算式里有小数,参与运算的数就必须至少有一个是浮点类型。
三种修法
1 | double a = (double)t / n; // 强制类型转换 |
⭐ 陷阱 2:向上取整 / 向下取整
| 场景 | 方向 | 写法 |
|---|---|---|
| 至少需要几个(不够也进位) | 向上 | (a + b - 1) / b |
| 最多能买几个(不够就舍) | 向下 | a / b |
例:走路 10 米,速度 3 米/分 → (10 + 3 - 1) / 3 = 4 分钟
⭐ 陷阱 3:浮点数不要用 == 比较
1 | if (a == 0.1 + 0.2) // ❌ 浮点几乎不可能精确相等 |
⭐ 陷阱 4:== 和 = 写错
1 | if (t == 4) // ✅ 比较 |
编译器可能只给警告,不报错。 防御写法:if (4 == t)(写错会立刻报错)
⭐ 陷阱 5:数据范围
| 范围提示 | 用什么 |
|---|---|
| 保证答案在 int 范围内 | int |
| 答案可能到 10^18 | long 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 | int p1 = (x % 2 == 0); // p1 是 1 或 0 |
“刚好一个成立” = 异或
1 | int b = (p1 != p2); // ✅ 一真一假 |
优先级
1 | ! > 比较 > |
不确定就加括号。
七、常见数学函数
1 |
| 函数 | 作用 | 例子 |
|---|---|---|
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,处理 intfabs在math.h,处理 double
⚠️ 开立方用 pow
1 | pow(v, 1.0 / 3.0) // ✅ 1.0/3.0 必须是浮点! |
⚠️ 本地 gcc 编译要加 -lm
1 | gcc a.c -o a -lm |
洛谷上不用管。
⚠️ 浮点误差
1 | (int)pow(v, 1.0/3.0) // 可能是 15.9999998 → 得到 15 ❌ |
八、字符串基础
1 | char s[100]; // 声明一个能装 100 个 char 的数组 |
⚠️ 容量 ≠ 长度
1 | char s[100086]; // 容量 100086 |
字符串末尾有隐藏的 '\0',所以 char s[5] 装不下 5 个字母。
逐字符处理
1 | for (int i = 0; i < n; i++) |
倒着打印
1 | for (int i = n - 1; i >= 0; i--) // ⚠️ 是 n-1 不是 n |
九、代码风格
缩进(Python 感的来源)
1 | int main() |
规律
{跟上一层对齐,或独占一行- 每深一层多 4 个空格
- 用空格不用 Tab(网页 IDE 里 Tab 会错位)
空行分组 ⭐
用空行把"读入 / 计算 / 输出"分成块 —— 这才是看着舒服的关键。
1 | int main() |
空格规则
1 | printf("%d\n", a + b); // ✅ 逗号后加空格,前面不加 |
逗号/分号前面不加空格,后面加一个;运算符两边各一个空格。
危险运算要放在安全分支里 ⭐
1 | if (t == 0) |
你曾经把 s / t 写在 if 前面 → t = 0 时直接崩。
十、调试与排错
⭐ 打印调试法
在代码里临时加 printf 看中间变量:
1 | printf("读到了: %s\n", s); |
调完记得删掉。
⭐ 手工验算法
写完关键逻辑,用具体数字代进去念一遍:
1 | apple / child → "14 除以 4" → 每人 3 个 ✔️ |
这个方法能抓出 90% 的"变量放反"错误。
自测清单(交之前扫一遍)
| 检查 | 说明 |
|---|---|
1. & | scanf 里每个变量都有吗? |
2. == vs = | 条件里写的是两个等号吗? |
| 3. 拼写 | 变量名前后一致?(大小写!lsat vs last) |
| 4. 分号 | 每行结尾都有吗? |
| 5. 括号 | 配对吗?数清楚了? |
| 6. 格式符 | %d 配 int?%lf/%f 配对了吗? |
| 7. 输入顺序 | 和题目的变量顺序一致吗? |
| 8. 尾行换行 | 多加 \n 了吗? |
9. return 0 | 写了吗? |
边界测试 ⭐
别只用样例测,想想极端情况:
t = 0?(除零)- 结果为负?(要不要归零)
- 数据超范围?(溢出)
- 空输入 / 单个元素?
十一、洛谷报错含义
| 缩写 | 全称 | 含义 | 常见原因 |
|---|---|---|---|
| AC | Accepted | 通过 | — |
| WA | Wrong Answer | 答案错 | 逻辑错、格式错、精度错 |
| RE | Runtime Error | 运行崩溃 | 漏 &、数组越界、除以 0 |
| TLE | Time Limit Exceeded | 超时 | 算法太慢 |
| CE | Compile Error | 编译失败 | 语法错误 |
| MLE | Memory Limit Exceeded | 内存超限 | 数组开太大 |
⭐ 关键认知
OJ 报的是"最终状态",不是"bug 清单"。
一个 WA 背后可能藏着好几个错误,改完一个还有下一个。
所以修 bug 是循环:改 → 交 → 看结果 → 改 → 交。
看报错信息
CE 时编译器会告诉你:
1 | error: 'child' undeclared; did you mean 'child3'? |
直接就是答案。 越是新手越该认真读报错。
警告也要看:
1 | warning: unused variable 'child3' |
“定义了但没用” —— 常常正好指出你写错了变量名。
十二、刷题清单
【入门1】顺序结构(✅ 已完成)
| 题号 | 题目 | 状态 |
|---|---|---|
| B2002 | Hello, World! | ⬜ |
| B2025 | 输出字符菱形 | ⬜ |
| P1000 | 超级玛丽游戏 | ⬜ |
| P1001 | A+B Problem | ✅ |
| B2005 | 字符三角形 | ✅ |
| P5703 | 苹果采购 | ✅ |
| P5704 | 字母转换 | ⬜ |
| P5705 | 数字反转 | ✅ |
| P5706 | 再分肥宅水 | ⬜ |
| P5708 | 三角形面积 | ✅ |
| P5707 | 上学迟到 | ✅ |
| B2029 | 大象喝水 | ✅ |
| P1425 | 小鱼的游泳时间 | ✅ |
| P1421 | 小玉买文具 | ✅ |
| P3954 | 成绩 | ✅ |
【入门2】分支结构(🔄 进行中)
| 序号 | 题号 | 题目 | 状态 |
|---|---|---|---|
| 1 | P2433 | 小学数学 N 合一 | ✅ |
| 2 | P5709 | 苹果和虫子 | ✅ |
| 3 | P5710 | 数的性质 | 🔄 当前 |
| 4 | P5711 | 闰年判断 | ⬜ |
| 5 | P5712 | Apples | ⬜ |
| 6 | P5713 | 洛谷团队系统 | ⬜ |
| 7 | P5714 | 肥胖问题 | ⬜ |
| 8 | P5715 | 三位数排序 | ⬜ |
| 9 | P5716 | 月份天数 | ⬜ |
| 10 | P1085 | 不高兴的津津 | ⬜ |
| 11 | P1909 | 买铅笔 | ⬜ |
| 12 | P5717 | 三角形分类 | ⬜ |
| 13 | P1422 | 小玉家的电费 | ⬜ |
| 14 | P1424 | 小鱼的航程 | ⬜ |
| 15 | P1888 | 三角函数 | ⬜ |
| 16 | P1046 | 陶陶摘苹果 | ⬜ |
| 17 | P4414 | ABC | ⬜ |
| 18 | P1055 | ISBN 号码 | ⬜ |
十三、踩过的坑速查
按踩坑顺序排列,每一个都是真实犯过的错。
| # | 题目 | 错误 | 教训 |
|---|---|---|---|
| 1 | B2005 | 输出多了尾行换行 | 尾行 \n 会导致 WA |
| 2 | P5703 | scanf("%d%d", x, y) 漏 & | 漏 & → RE |
| 3 | P5705 | 循环写成 i = 3,漏了最大下标 | 下标是 0 到 len-1 |
| 4 | P5708 | s / S 大小写不一致 | C 大小写敏感 |
| 5 | P5708 | %.lf(字母 l)当成 %.1f(数字 1) | 这俩长得太像 |
| 6 | P1421 | int price = 19 漏分号 | 交前扫一眼分号 |
| 7 | P3954 | %d 打印 double 表达式 | 类型必须匹配格式符 |
| 8 | B2029 | scanf 又漏 & | 第二次踩同一个坑 |
| 9 | P2433 | if (t = 4) 少一个等号 | 赋值 vs 比较 |
| 10 | P2433 | child 写成 child3 的错拼 | 看编译器警告 |
| 11 | P2433 | 4 / 3 整数除法 | 球体积少 1.33 倍 |
| 12 | P2433 | cout 输出精度 ≠ printf("%f") | cout 相当于 %g |
| 13 | P2433 | child / apple 变量放反 | 用"念一遍"法自查 |
| 14 | P5709 | scanf 顺序写成 m s t(题目是 m t s) | 顺序必须对齐题面 |
| 15 | P5709 | s / t 写在 if (t == 0) 之前 | 危险运算要放安全分支里 |
| 16 | P5709 | 变量名打错 lsat | 警告"未使用变量"是线索 |
| 17 | P5709 | 忘了"正在吃的苹果"也要减 | 读题注意定语"完整的" |
十四、常用套路总结
套路 1:统一单位
时间/钱/长度加减,先统一到最小单位,算完再换回来。
1 | int start = a * 60 + b; // 小时分 → 分钟 |
好处:避免"借位"的麻烦。
套路 2:桶计数(数组当下标)
1 | int ct[26] = {0}; // 计数数组,一定初始化 |
把 26 个 if 变成一行。
套路 3:字符 ↔ 数字
1 | s[i] - 'a' // 字符 → 下标 |
套路 4:倒推
“经过一系列操作到达某个状态,问初始状态” → 从已知终点倒着推。
逆向运算:加变减、减变加、乘变除、除变乘,顺序也要倒过来。
例子(猴子吃桃):正向是 ÷2 - 1,逆向就是 +1 ×2。
套路 5:把数学式翻译成 C
老老实实按运算符优先级加括号:
1 | // √(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 之后