【OJ技巧】DSACPP pa-book中的一些提示
基本上是被教做人的感觉了……哎 菜鸡没有未来
附:输入输出技巧
-
判断输入结束
有些编程作业题并未指明测试数据的组数,此时需要自己判断输入结束。其实,根据题意正确处理输入数据也是同学们在这门课中需要练习的编程能力之一。
处理输入的方法很简单,使用 C++ 风格的 cin,可以这样写
int a, b, c; while (cin >> a >> b >> c) { /* blablabla */ }
如果使用 C 风格的 scanf() 函数,则可根据其返回值做出判断,具体地可以这样写:
int a, b, c; while (scanf("%d%d%d", &a, &b, &c) != EOF) { /* blablabla */ }
当格式输入流读到文件末尾时会返回 EOF,于是 while 退出。
-
过滤空白字符 有些题目是这样的输入格式:
E 6 M E 2 M
即字母、数字混输。如果用 getchar 或 scanf 的 %c 参数,会受到行末空白符(比如空格、换行等)的困扰。cin 到字符串、scanf 的 %s 参数都会自动过滤空白符。使用标准库的功能来过滤空白符,会使程序逻辑更清晰。示例代码:
char buf[8]; int x; while (scanf("%s", buf) != EOF) { switch (buf[0]) { case 'E': scanf("%d", &x); /* balabala */ } }
-
I/O 缓存加速
使用 cstdio 中的 setvbuf 函数,设置一个较大的 I/O 缓冲区,有时有很好的加速效果。用法示例:
setvbuf(stdin, new char[1 << 20], _IOFBF, 1 << 20); setvbuf(stdout, new char[1 << 20], _IOFBF, 1 << 20);
注意:一、必须在所有 I/O 操作前调用这个函数,不能在已读入一些数据后再设置缓存;二、如果设置了 I/O 缓存,控制台输入输出可能失效。
-
重定向
为便于反复测试及再现运行过程,可采用输出、输入重定向的方法。 你只需事先将输入数据存成文件,运行时系统会自动从中获取输入。其效果完全等同于你从(作为默认输入流的)键盘逐项输入。 类似地,你也可以指定另一文件,并使运行的结果自动存入其中。其效果完全等同于从(作为默认输出流的)屏幕截取输出结果。 重定向的好处很多:可以避免手工输入的出错,忠实可靠地重复测试;可以实现大规模数据的输入;可以完整精确地记录程序的输出,以便事后的对比分析;可以省去默认输入、输出流占用的大量时间,更加准确地测量程序的执行效率。
-
方法一:修改源文件,指定重定向的输入、输出文件
例如,若希望从文件 input.txt 中获取输入,将输出保存到文件 output.txt 中,则可在主程序开头增加如下语句:
#ifndef _OJ_ freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); #endif
注意:如果用 C++ 风格的 cin / cout 的话,还要在前面引用头文件的部分加入
#include <cstdio>
。OJ 在编译程序的时候会定义名为
_OJ_
的宏,所以上面这段语句会在 OJ 运行的时候被跳过。 -
方法二:在 IDE 中通过设置命令行,重定向输入、输出文件 以 Visual Studio 为例,可打开对应工程的“属性页”,在“配置属性”下的“调试”页,设置“命令行参数”。
输入参数不多时,可直接键入。例如 ADD 一题,键入“100 200”即可。若其中包含特殊字符,则需以'^'引导,或者使用一对半角括号消除歧义。
若输入参数多,且不止一行,则可将其存成一个文件。比如,可在“命令行参数”中键入:
< D:\test\input.txt
(注意起始字符"<"不能省略)为将程序的输出保存至指定文件,可在“命令行参数”中继续键入:
> D:\result\output.txt
(同样地,起始字符">"也不能省略)若不希望覆盖文件原有的内容,只需用">>"替换以上的">",即可将每次运行的输出追加至 D:\result\output.txt。
输入、输出的重定向可同时采用并生效。比如可在“命令行参数”中键入:
< D:\test\input.txt >> D:\result\output.txt
重定向文件的具体路径与文件名可自行选择,但若包含空格,则需使用一对半角引号消除歧义,比如:
< "D:\my test\input.txt" >> "D:\my result\output.txt"
-
-
帮助资料
关于输入输出的进一步问题,可以自己查阅相关手册或资料。
也可参考标准手册,以上输入输出方法都是 C / C++ 标准输入输出,在 manual 中都有详细介绍。
cin:http://www.cplusplus.com/reference/iostream/cin/
附:一些常见逻辑错误
-
swtich 里忘记加 break,导致多个case 后的语句都被执行。
-
int *a = new int[n]
误写成int *a = new int(n)
。前者申请了 n 个 int 的空间;而后者只申请了一个 int 的空间,并初始化为 n。 -
在函数里开了很大的数组,导致运行时栈溢出。错误示例:
int main() { int a[10000000]; return 0; }
可以使用动态内存分配,避免栈溢出,例如:
int main() { int *a = new int[10000000]; delete[] a; return 0; }
-
计算溢出。错误示例:
int a = 1000000, b = 1000000; long long c = a * b;
程序会先在 int 范围内计算
a * b
,最后把溢出后的结果赋值给 c。正确写法是c = (long long)a * (long long)b
。 -
有返回值的函数漏 return 语句。
行为是不确定的,有的编译器会恰巧返回你想要返回的变量,而一般不会。
-
memcpy 的源地址和目标地址有重叠。
附:Visual Studio msvc 与 GNU gcc 的差异
-
msvc 可能会自动 include 一些头文件,gcc 编译提示函数找不到。
-
使用 scanf 等函数会警告
not safe(warning 4996)
,msvc 推荐使用 scanf_s ,但是这个不属于 C / C++ 标准,gcc 没有。 -
gcc 也没有 itoa(数字转换为字符串的函数)。
-
gcc 上,模板类继承模板类,two phase name lookup,调用父类函数会提示找不到,需要用 this-> 调用。
-
gcc 禁止 void main。main 函数必须 return 0,如果返回值非 0,也会被认为是运行时错误,不得分。
-
http://dsa.cs.tsinghua.edu.cn/oj/static/submission_result_explanation.html 列出了一些常见编译错误的解决方法。