关于 OJ 的常见问题 Q&A 汇总贴 (23-09-13更新)

lr580 发表于 2年前 · 关联问题 A+B 问题

不定期更新,仍有疑问欢迎回帖提出 QwQ

目前已解答问题:

为什么本地正确提交错误

首先提醒,当你查看运行代码测试点时,答案不是你代码的输出,答案是正确输出参考,你的代码输出是标准输出

通常而言有两种可能性:①数组等下标越界;②数组未初始化。

这两种情况都会使得你访问未赋值的内存区。不同的计算机对未赋值内存区的值有不同的处理方式,Windows 一般可能会得到值为 0,所以看起来跟初始化了一样,所以本地有可能看起来是正确的,但 Linux(即OJ环境)更有可能不会给你初始化,所以得到的值认为是随机的。

解决办法:仔细检查你的代码有无下标越界/未初始化。出现这种情况一定不是 OJ 的问题。

在 main 函数内开数组导致运行错误(RE)怎么办

变量分为全局变量和局部变量。其中局部变量的总内存大小有限(大约是几 MB 不到),而所有在函数内(main 函数也算)定义的变量和普通数组变量(vector 等除外)属于局部变量。

假设使用 int,则一个元素 4B,设数组大小是 n,假设最大上限为 1MB,有 4nB=2^{20}B,n\approx2.6\times10^5。即建议局部变量最大不应超过 10^5 量级。

而全局变量总内存大小的限制远大于局部变量限制(每道题不一样,请看每题的题面描述)。因此,建议把所有大数组使用全局变量。例如:

#include <stdio.h> const int maxn = 1e6 + 10; int x[maxn]; //正确的 int main() { int y[maxn]; //可能错误的 //其他解题代码 return 0; }

判题机制是什么/为什么超时,超内存,编译错误

采用黑盒测试。即:给定多个测试用例(又称测试点)作为文件输入(记为 inf,注意不是键盘输入,你可以认为采用了 freopen 将标准输入重定向到了读文件),对每个用例,运行程序,并将程序输出全部写入到另一个文件(记为 ouf,注意不是输出到屏幕,也就是在真实判题中输入输出分离的,而不是一并混合显示在屏幕上)。然后比对该用例的答案输出(记为 ans)。通常而言,比对逻辑为:对 oufans 的先去掉每一行行末空白字符(换行和空格等)及结尾的连续空白字符(含连续空行),然后逐字符比较每一行,若比较一致,则答案正确。若不一致,显示答案错误或输出格式错误。

对于一些特殊题目,会有 SPJ(special judge),这些题目的判题逻辑各不相同,视题意而定。区别在于不是逐字符比较每行,而是用一个出题人写好的 SPJ 程序以 inf,ouf,ans 三个文件为输入,输出通过或答案错误两种状态其一。

一般而言,在执行多个测试用例时,遇到首个非答案正确的用例时,会马上以该用例的结果反馈。如假设代码在第一个测试点答案正确、第二个测试点答案错误、第三个测试点将运行出错,则结果显示答案错误。

如果编译不通过,则一个测试点都不会执行,并返回错误信息。下表列出常见的不能通过编译的函数与模块:(欢迎补充)

  1. C/C++ gets 函数。理由:C++11 及更高版本已废置。取代:使用 fgetscin.getlinegetline 函数。
  2. C/C++ shuffle 函数。理由:C++14 及更高版本已废置, C++17 移除。取代:使用 random_shuffle 函数。
  3. register 变量取地址 &x(寄存器变量不能取地址)。建议:编译器一般会自动优化,不建议手动加这些

如果执行过程引发异常,会触发运行错误。常见的错误有:

  1. 下标越界
  2. 递归过深(本 OJ 的最大递归栈约为 128 MB,折合约 1.3\times10^7 层递归函数,这里假设一个函数算 3int 空间(具体视不同函数而定))。
  3. 忘记删除本地调试的 freopen 等文件读写
  4. main 等有返回值的函数没全部分支都 return (即便你没用到返回值也要 return)(请注意这种代码不加 O2 加速能过,加了 O2 加速就 RE)

如果显示输出超限,则代表某个测试点你的 ouf 长度至少是 ans 的两倍。建议检查你是否输出了多余内容,常见原因有 debug 输出代码未删除或死循环。

如果执行用时超过了题面约定的用时,会马上中断程序运行,并显示运行超时,且运行状态的显示用时是一个近似于约定上限的用时,并不代表你的程序最终只运行了这么久,若不中断,运行超时时实际运行时间会大于等于状态里显示的时间。同理,若内存超限时实际占用内存也会大于等于状态里显示的内存

如:对下面两个状态

代码分别为:

#include <stdio.h> const int maxn = 2e8; int x[maxn]; int main() { int a, b; x[0] = 1234567, x[1] = 7654321; for (int i = 2; i < maxn; ++i) { x[i] = x[i - 1] * x[i - 2]; } printf("%d", x[maxn - 1]); return 0; }

#include <stdio.h> int main() { int a, b; for(;;); scanf("%d%d", &a, &b); printf("%d", a + b); }

显然第一份代码内存为 2\times10^8\times4\div2^{20}MB\approx763MB。第二份代码运行时间为无限(死循环)。

比赛榜单的排名是如何排的

比赛共有三种模式(在比赛信息页可看),分别是 ICPC, OI 和 IOI。

对 ICPC,每道题只有全部测试用例都通过,才能通过该题。通过该题的罚时记为从比赛开始到首次通过本题的分钟数加上首次通过本题前的错误提交(不含编译错误)次数乘以罚时单位。通常罚时单位为 20,若有特殊,会在比赛说明里提出。总罚时已通过的各题罚时之和。排序依据是先按过题数从多到少排,过题数相同按总罚时从少到多排。

一个计算罚时的例子:

1335\approx(187+1\times20)+(145)+(271+1\times20)+(241+6\times20)+(289+2\times20) (之所以是约等于,是因为每题的分钟后面还有秒数,若干题的秒可以叠加为额外的分钟) 328=(146)+(87)+(75+1\times20) 对作业模式,可以认为与 ICPC 模式一致。特别地,当比赛时间为一直开放时,罚时只计算错误提交次数。

对 OI 模式,每道题将测试点分为若干个组(一般题目有描述)。若一个组的全部测试点通过,则可得到该组的分值。一题的分值为各组得分和。排名依据是总得分,得分相同排名并列。

IOI 模式计分方式与 OI 模式相同,唯一区别在于 OI 模式赛时不能看到提交的代码的反馈结果,其他比赛模式均可。

为什么读入数据会死循环/超时/与本地不一致

注意测试用例里输入的最后一个字符不保证是回车,也不保证回车必然是 \n,也可能是 \r\n 的组合。

(以 1000. A+B 问题 为例)输入文件可能是:1 2,而不是:1 2\n

对于不定数目的输入,建议使用 EOF 来判定 break 而不是使用 \n

对于多个单行不定数目的输入,建议使用 fgets 等读入整行的函数输入,然后进行字符串处理如使用 sscanf 函数。

参考程序:(以 1033. A+B 输入输出练习 I 为例)

#include <stdio.h> int main() { int a, b; while (EOF != scanf("%d%d", &a, &b)) printf("%d\n", a + b); return 0; }

注意,使用 fgets 时,如果是遇到 \n 而返回的,会包含 \n;如果是遇到 EOF 而返回的,则不会包含 EOF。我们不保证任何一道题目的最后输入一定以 \n 结束,可能直接以 EOF 结束。它们的区别如下:

因此,如果需要使用 fgets,我们建议你特判最后一个字符是否是 \n,如果是,有效字符串长度要减一。因为,对于本地而言,通常总是上图图二情况,对提交 OJ 而言,测试数据可能更多是上图图一情况,所以会出现本地与提交的输入看似不一致的情况。

如何修改学号/忘记密码无法找回

请联系任意一个助教、任课老师、香农先修班负责人、软协技术部成员或管理员。

讨论区排版错误

参见 这个帖子

另,讨论区的发帖若有人回帖,不会主动通知,请自行主动查看。

Java过不了编译

  • 不要使用 package 代码
  • 类名务必为 public class Main

参考正确代码示例:here and here

131452lin 发表于 2年前

前排cy

1014 发表于 2年前

好耶

陈宝霖 发表于 2年前

6

我超,佬