- 2022tysc0127 的博客
关于题解的建议
- @ 2023-7-23 11:27:34
前言
一年一度的暑假又到了。oiclass 第四届夏令营也来了。相信不少人 AC 题目之后都有一种喜悦感,想通过发题解分享自己的思路和代码。而不规范的题解不但不美观,还可能误导新手,因此,这里写一些题解的建议。
本人文采不好及水平有限,请见谅。本文较长,还请大家耐心阅读。
一、题解的内容
一篇题解不应该只有代码。如果你只给出代码,这对于新手来说很难读懂。
一篇好的题解可以包含以下内容:
- 题目大意(可有可无)
- 你的思路(必须有)
- 代码(可有可无)
- 图片 / 外部链接(可有可无)
- 其他(包括但不限于用到算法的讲解)
此外,题解不应该包括太多的无意义内容,比如求赞、小游戏、闲聊、吐糟。
对于模板题,还要对这题用到的算法进行解释,以便读者理解。
二、正确使用 Markdown
正确的使用 Markdown,可以让文章主次分明,展示美观。而错误的 Markdown,会让人阅读困难。
如果你不清楚什么是 Markdown,可以自行百度。
实际上,每当你发帖 / 发博客的时候,你都会发现右边有一栏字(如图):

这里是一些基础 MarkDown 用法。你在发题解的时候可以看着来写。
此外,还有一些 Markdown 语法,在 oiclass 都已经有了快捷按钮,你也可以自己手打。这里介绍几种图中没有提及的 Markdown:
- 标题
标题在 oiclass 的快捷键是 Ctrl + Alt + 中的一个数字。标题分为一级标题到六级标题。其中标题的级别越大,字体也越大。面对有很多部分的内容时,可以使用标题,但不要滥用标题。我自己的偏好是三级标题,包括这篇文章用的也是三级标题。 标题源码:
# 一级标题
## 二级标题
### 三级标题
#### 四级标题
##### 五级标题
###### 六级标题
效果:
一级标题
二级标题
三级标题
四级标题
五级标题
六级标题
注意:无论是几级标题,"#" 后面都应该加空格。
- 粗体、斜体与删除线
粗体、斜体、删除线在 oiclass 的快捷键分别是 Ctrl + B、Ctrl + I(是大写字母 I)与 Ctrl + D。他们的用处应该我不用多说了吧。
源码:
这是**粗体**、*斜体*和~~删除线~~。
效果:
这是粗体、斜体和删除线。
需要注意的是,标点符号一般不会放在粗体、斜体、删除线里。否则就会像下面一样:
源码:
**这是加粗字体。**这是不加粗字体。
效果:
**这是加粗字体。**这是不加粗字体。
你会发现,“这是加粗字体”并没有被真正的加粗。而解决方案吗,就是在后面的“**”后面加上一个空格。
改进后的源码:
**这是加粗字体。** 这是不加粗字体。
效果:
这是加粗字体。 这是不加粗字体。
虽然成功了,但是不太美观。因此不建议把标点符号放在粗体、斜体、与删除线里。
关于粗体的一个注意事项:一篇题解不要用太多的粗体,这样会显得你的文章没有重点。
- 有序列表与无序列表
有序列表和无序列表都可以来表达并列的信息。他们在 oiclass 的快捷键分别是 Ctrl + L 和 Ctrl + O。
源码:
+ 无序列表写法一
+ 无序列表写法一
- 无序列表写法二
- 无序列表写法二
* 无序列表写法三
* 无序列表写法三
1. 有序列表第一项
2. 有序列表第二项
3. 有序列表第三项
效果:
- 无序列表写法一
- 无序列表写法一
- 无序列表写法二
- 无序列表写法二
- 无序列表写法三
- 无序列表写法三
- 有序列表第一项
- 有序列表第二项
- 有序列表第三项
和标题的注意事项一样,列表的"+/-/*/数字"后面都要加空格。
- 表格
表格在 oiclass 的快捷键是 Ctrl + M。
源码:
居左的表格:
| 表头 1 | 表头 2 |
| :--- | :--- |
| 1 | 2 |
居中的表格:
| 表头 1 | 表头 2 |
| :---: | :---: |
| 1 | 2 |
居右的表格:
| 表头 1 | 表头 2 |
| ---: | ---: |
| 1 | 2 |
效果:
居左的表格:
| 表头 1 | 表头 2 |
|---|---|
| 1 | 2 |
居中的表格:
| 表头 1 | 表头 2 |
|---|---|
| 1 | 2 |
居右的表格:
| 表头 1 | 表头 2 |
|---|---|
| 1 | 2 |
注:表格第二行中的"-"号数量不需要一定是 3 个,整行数量一致即可。
- 空格的使用
大家会发现,我在这篇文章的一些地方用了空格。
那什么时候要用空格呢?
中文与西文字符或公式之间以一个半角空格隔开,但标点符号与西文字符或公式间不要加空格。
相信肯定会有人不理解,那就举个例子吧。
错误的:
这道题用时间复杂度是$O(n^2)$的代码无法通过,要用二分优化。
这道题用时间复杂度是的代码无法通过,要用二分优化。
正确的:
这道题用时间复杂度是 $O(n^2)$ 的代码无法通过,要用二分优化。
这道题用时间复杂度是 的代码无法通过,要用二分优化。
用了空格不会让公式显得过于拥挤,但是 oiclass 不强求,可用可不用。
三、 的使用
在上面的例子中,出现了 $O(n^2)$ 这样的语句。其实,他就是 。
是一个支持 HTML 的轻量级的数学公式引擎,它由 Khan Academy 开发,使用起来也非常简单。
- 的使用场景
并不是一切东西都该放在公式中的。滥用公式会导致渲染速度下降,排版混乱等后果。
所以什么东西不该放在公式中呢?
- 中文一般不要放在 中。
- 算法名,人名等非公式内容一般不要放在 中。
- 行内的程序代码(包括程序函数名称,变量类型,完整语句等)应该用行内代码框表示,而不放在公式中。
.
- 常见的 语法。
注:以下内容只是一些常用的 语法。
- 运算符
$+ - \mp \pm \times \div\sqrt{n}\sqrt{3}{n}$
$+\quad -\quad \mp\quad \pm\quad \times\quad \div\quad \sqrt{n}\quad\sqrt[3]{n}$
- 集合
$\{ \} \in \not\in \varnothing \cap \cup$
$\{\quad \}\quad \in\quad \not\in\quad \varnothing\quad \cap\quad \cup$
- 关系符号
$= \ne \approx \le \ge < > \sim$
$=\quad \ne\quad \approx\quad \le\quad \ge\quad <\quad >\quad\sim$
- 几何符号
$\perp 45^\circ$
- 逻辑符号
$\forall \exists \nexists \therefore \because \And \lor \land \lnot$
$\forall\quad \exists\quad \nexists\quad \therefore\quad \because\quad\And\quad \lor\quad \land\quad \lnot$
- 上标与下标
$a^2 a_{1+1} a^2_1 {}^1_2 a^1_2$
- 特殊符号
$\bigcirc \Box \blacksquare$
- 求和、求积
$\sum\limits_{i=1}^n a_i \prod\limits_{i=1}^n a_i$
$\sum\limits_{i=1}^n a_i\quad \prod\limits_{i=1}^n a_i$
- 取模
$\% \bmod a\equiv1\pmod p\mid \nmid$
$\%\quad \bmod\quad a\equiv1\pmod p\quad\mid\quad\nmid$
- 分数
$\frac{1}{2} \dfrac{1}{3} \dfrac{1}{x+\dfrac{1}{y+\dfrac{1}{z}}}$
$\frac{1}{2}\quad \dfrac{1}{3}\quad \dfrac{1}{x+\dfrac{1}{y+\dfrac{1}{z}}}$
- 矩阵
$\begin{bmatrix}a&b\\c&d\end{bmatrix}$
$\begin{Bmatrix}a&b\\c&d\end{Bmatrix}$
$\begin{vmatrix}a&b\\c&d\end{vmatrix}$
- 条件定义(如分段函数)或方程组
$f(x)=\begin{cases}x+1&x>1\\x^2-1&x\le1\end{cases}$
$\begin{cases}x^2-3x+1=3\\x^3-5x^2+x-10=1\end{cases}$
$\begin{cases}x^2-3x+1=3\\x^3-5x^2+x-10=1\end{cases}$
- 多行等式
$\begin{aligned}C_n^2&=\dfrac{A_n^2}{A_2^2}\\&=\dfrac{n\times(n-1)}{2}\end{aligned}$
$\begin{aligned}C_n^2&=\dfrac{A_n^2}{A_2^2}\\&=\dfrac{n\times(n-1)}{2}\end{aligned}$
- 空格
$a\,b a\ b a\quad b a\qquad b$
$a\,b\quad\quad a\ b \quad\quad a\quad b \quad\quad a\qquad b$
- 使用误区
.
- 要用 Roman 字体来写函数。
错误的:
$lcm(a,b)\times gcd(a,b)=a\times b$
正确的:
$\operatorname{lcm}(a,b)\times\gcd(a,b)=a\times b$
简单来说,就是像 这样已经定义好的函数就在前面加 \,否则就用 \operatorname{} 括起来函数名称。
- 对于字符串常量,要使用打印机字体。
错误的:
$s=IAKIOI$
正确的:
$s=\texttt{IAKIOI}$
- 对于非公式内容,要使用
\text{}。
错误的:
$f(x)=\begin{cases}1&x\in prime\\0&otherise\end{cases}$
$f(x)=\begin{cases}1&x\in prime\\0&otherise\end{cases}$
正确的:
$f(x)=\begin{cases}1&x\in \text{prime}\\0&\text{otherise}\end{cases}$
$f(x)=\begin{cases}1&x\in \text{prime}\\0&\text{otherise}\end{cases}$
- 公式不是写代码的地方
我们写的是非常正规的数学公式,因此不要在非代码区域使用任何程序设计语言的表示方式。
错误的:
x++
a=x\%p
正确的:
x\gets x+1
a=x\bmod p
- 数组的表示
错误的:
$a[i][j]$
这里错误的原因和上一条一样。
正确的:
$a_{i,j}$ 或 $a(i,j)$
或
四、代码的要求
人与人之间的代码风格差异还是挺大。不过题解对代码风格的要求并不高,只需要可读性好即可。
不要写恶搞题解。如明显超出本题范畴的题解。反例就是 A+B Problem 的题解区里的部分题解。
作为一篇题解,代码肯定不能乱写。此外,代码应该要有恰当的注释,避免喧宾夺主。
下面是一个典型的注释喧宾夺主的例子(/* */ 注释的部分指出问题所在):
#include<bits/stdc++.h>//万能头文件
/* 这个头文件是万能头文件很重要吗? */
using namespace std;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);//优化cin,cout,险些TLE。
/* 同前。 */
int a[100005],n;
cin>>n; // 输入礼物数。
/* 题目输入格式里已经把输入的每个数字的含义写的很清楚了。 */
for(int i=1;i<=n;i++)//代表n个礼物。
/* 还能是几个礼物? */
{
cin>>a[i];//输入坐标。
/* 同前。 */
a[i]=min(a[i]-1,1000000-a[i]);//取最小值min。
/* 是当读者完全不知道 min 函数吗? */
}
sort(a+1,a+1+n);//排序后取最大值。
/* 同前。 */
cout<<a[n];//将最大值输出。
/* 同前。 */
return 0;//结束。
/* 不是结束还能是啥? */
}
/* 总结:请不要把读者当完全没学过语言的傻子。 */
以下是一个可读性不大的例子:(洛谷 P2357)
#include<bits/stdc++.h>
#define endl '\n'
typedef long long ___________;
using namespace std;
___________ _____,_________,_[200001],_1,_2,_3,__[200001],___[200001],________;
___________ _______(___________ _){return _ & (-_);}
void ____(___________ _,___________ ______){for(___________ __________ = _;__________ <= _____;__________ += _______(__________))__[__________] += ______,___[__________] += (_ - 1) * ______;}
___________ ______(___________ x){___________ ______ = 0;for(___________ __________ = x;__________ >= 1;__________ -= _______(__________))______ += x * __[__________] - ___[__________];return ______;}
int main(){
cin >> _____ >> _________;
for(int __________ = 1;__________ <= _____;__________++)cin >> _[__________],____(__________,_[__________] - _[__________ - 1]);
while(_________--){
cin >> ________;
if(________ == 1)cin >> _1 >> _2 >> _3,____(_1,_3),____(_2 + 1,-_3);
else if(________ == 2)cin >> _3,____(1,_3),____(2,-_3);
else if(________ == 3)cin >> _3,____(1,-_3),____(2,_3);
else if(________ == 4)cin >> _1 >> _2,cout << ______(_2) - ______(_1 - 1) << endl;
else if(________ == 5)cout << ______(1) << endl;
}
}
此外,这个代码也没有注释。
后记
写这个博客写了大半天了。我写这个的起因是看到了这个讨论,我想:我也得给 oiclass 做点贡献。
写这个博客的时候参考了很多文章,也发现了自己的许多不足。
希望大家写题解的时候能参考这篇文章,不要写格式、内容不对的题解。
我最近看到了有人抄题解,我希望大家不要养成这种坏习惯,自己想思路自己写代码。
最后说一句:写题解未必是一件简单的事情。如果你想写一篇优质的题解,就必须为此付出更多的时间与精力。在写题解之前,你必须准备好耗费一个小时甚至几个小时的情况。
那这篇博客就到此结束了。