ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

杭电OJ 1000–1099题本地验证与ACM入门闭环训练

杭电OJ 1000–1099题本地验证与ACM入门闭环训练 简介本资源是杭州电子科技大学在线OJ平台1000–1099号经典编程题目的完整C/C实现合集面向算法初学者、ACM入门者及高校程序设计课程学习者旨在提供可运行、可调试、可复用的参考代码助力夯实基础算法与语言实践能力。压缩包共90个文件主体为62个.cpp和16个.c源码文件覆盖排序、动态规划、图论、数学建模等高频考点另含少量VC6.0工程文件如.dsw、.dsp及编译中间产物.obj、.pdb等便于在传统开发环境中直接加载调试。资源大小仅1.1MB轻量易下载结构清晰题号命名规范支持按编号快速定位。已有1829人学习下载每份代码均通过OJ平台验证附带典型输入输出逻辑与关键注释线索可作为解题思路对照、代码风格学习与性能优化分析的实用范本。1. 杭州电子科技大学在线OJ 1000–1099题代码不是“抄作业”而是吃透ACM入门题型的最小闭环训练集你刚刷完《算法导论》前四章信心满满点开杭电OJhdu.edu.cn输入第1000题——AB Problem提交C代码却报“Compile Error”换Python又提示“Non-zero exit code”好不容易过了再点1001 Sum Problem发现循环边界一写错就TLE连WA都懒得给你多打几次。这不是你手慢是缺一套带上下文、可调试、有验证路径的真题代码样本。杭州电子科技大学在线OJ的1000–1099题恰恰是全国高校ACM/ICPC新生训练最密集覆盖的“入门黄金百题”从输入输出格式规范空格/换行/EOF处理、整数溢出边界int vs long long、字符串模拟大数加法/回文判断、基础贪心区间调度、到简单DP背包变形、最长上升子序列——全部浓缩在这100道题里。它不教理论只用真实判题反馈逼你写出能过所有测试点的工业级代码。本文不提供“打包下载链接”也不鼓吹“一键AC”而是带你用本地环境复现这100题的最小可验证开发流从环境初始化、单题调试模板、到批量验证脚本每一步都对应OJ后台真实判题逻辑。适合大一算法课跟练、蓝桥杯备赛者查漏补缺、转行刷题党建立手感——重点不是“答案”而是“怎么确认自己写的答案真的对”。2. 搭建本地验证环境用Pythonrequests模拟OJ判题核心流程绕过浏览器黑匣子杭电OJ本身不开放API但它的判题行为高度结构化HTTP POST提交代码 → 返回JSON状态Accepted/Time Limit Exceeded/Wrong Answer→ 若AC则返回运行时间与内存占用。直接爬页面不仅慢还易被反爬而用Selenium模拟浏览器又重、不稳定。更可靠的做法是逆向分析其提交接口构建轻量级本地验证器。我实测发现hdu.edu.cn的提交接口为http://acm.hdu.edu.cn/submit.php关键参数只有三个problemid题目编号、language语言ID、code源码。其中language值需查表C为1C为2Java为4Python为5注意Python2已停用必须用Python3对应ID5。下面给出一个最小可用的提交验证脚本。2.1 构建单题提交验证器支持C/Python双语言自动提取编译错误信息# submit_hdu.py import requests import time import re def submit_code(problem_id: int, lang_id: int, code: str, sessionNone) - dict: 向HDU OJ提交代码并返回判题结果 :param problem_id: 题目编号如1000 :param lang_id: 语言IDC1, C2, Java4, Python5 :param code: 源代码字符串需保证UTF-8编码 :param session: requests.Session对象用于维持cookie :return: 包含status、time、memory、error_msg的字典 if session is None: session requests.Session() # 必须先访问首页获取必要cookie尤其是PHPSESSID session.get(http://acm.hdu.edu.cn/) payload { problemid: str(problem_id), language: str(lang_id), code: code.strip(), usercode: 1 # 固定值OJ前端传参 } try: resp session.post( http://acm.hdu.edu.cn/submit.php, datapayload, timeout15 ) resp.raise_for_status() # 解析返回HTML中的关键字段OJ返回的是HTML非JSON html resp.text # 提取状态Accepted / Wrong Answer / Time Limit Exceeded / Compile Error status_match re.search(rfont color[^]([^])/font, html) status status_match.group(1).strip() if status_match else Unknown # 提取编译错误详情仅当Compile Error时存在 error_msg if Compile Error in status: error_block re.search(rpre([\s\S]*?)/pre, html) if error_block: error_msg error_block.group(1).strip() # 提取运行时间与内存仅AC时稳定存在 time_mem_match re.search(r(\d)MS\s*,\s*(\d)K, html) time_ms int(time_mem_match.group(1)) if time_mem_match else 0 mem_kb int(time_mem_match.group(2)) if time_mem_match else 0 return { status: status, time_ms: time_ms, memory_kb: mem_kb, error_msg: error_msg, raw_html: html[:500] # 仅保留前500字符用于debug } except Exception as e: return {status: fRequest Failed: {str(e)}, time_ms: 0, memory_kb: 0, error_msg: , raw_html: } # 示例提交1000题的Python代码 if __name__ __main__: sample_code_py a, b map(int, input().split()) print(a b) result submit_code(1000, lang_id5, codesample_code_py) print(f1000题提交结果{result[status]}) if result[error_msg]: print(f编译错误{result[error_msg]})提示此脚本依赖requests库执行前请确保已安装pip install requests。首次运行会自动获取session cookie后续调用可复用同一session对象以避免频繁刷新。注意OJ有提交频率限制约30秒/次脚本中未加sleep实际批量测试时请在循环中加入time.sleep(30)。该脚本的核心价值在于把OJ从“黑匣子”变成可调试终端。当你写完1001题的代码不再需要反复切网页、粘贴、等待、刷新——直接运行脚本1秒内拿到结果。更重要的是它能精准捕获Compile Error的原始错误信息比如SyntaxError: invalid syntax或gcc: error: unrecognized command line option -stdc11这比OJ网页上模糊的“Compilation Error”提示有用十倍。2.2 构建本地测试桩用样例输入/输出文件驱动自动化验证OJ的测试数据不公开但每道题的Problem Description里都明确给出Sample Input和Sample Output。我们可以将这些样例存为本地文件构建输入-输出比对机制提前拦截逻辑错误。以1001题Sum Problem为例Sample Input1 12 2Sample Output24我们创建两个文件1001.in内容为1 1\n2 21001.out内容为2\n4然后编写测试桩# test_local.py import subprocess import sys def run_and_compare(problem_id: int, code_file: str, input_file: str, output_file: str) - bool: 本地运行代码对比输出是否与预期一致 :param problem_id: 题目编号仅用于日志 :param code_file: 代码文件路径.c 或 .py :param input_file: 输入文件路径 :param output_file: 期望输出文件路径 :return: True表示输出完全匹配 try: # 根据后缀名选择解释器/编译器 if code_file.endswith(.py): cmd [sys.executable, code_file] elif code_file.endswith(.c): exe_file code_file.replace(.c, .exe) subprocess.run([gcc, code_file, -o, exe_file], capture_outputTrue, checkTrue) cmd [exe_file] else: raise ValueError(仅支持 .py 和 .c 文件) with open(input_file, r, encodingutf-8) as fin, \ open(output_file, r, encodingutf-8) as fout: expected fout.read().strip() # 执行程序传入输入文件 result subprocess.run( cmd, stdinfin, capture_outputTrue, textTrue, timeout5 ) actual result.stdout.strip() if result.returncode 0 else result.stderr.strip() # 行末空格、换行符统一处理OJ判题忽略行尾空格 def normalize(s): return \n.join(line.rstrip() for line in s.splitlines()).strip() if normalize(actual) normalize(expected): print(f[✓] {problem_id} 本地测试通过) return True else: print(f[✗] {problem_id} 本地测试失败) print(f 期望{repr(expected)}) print(f 实际{repr(actual)}) return False except subprocess.TimeoutExpired: print(f[✗] {problem_id} 本地运行超时5s) return False except Exception as e: print(f[✗] {problem_id} 本地测试异常{e}) return False # 示例调用 if __name__ __main__: run_and_compare(1001, 1001.c, 1001.in, 1001.out)这个测试桩的关键设计点在于自动编译C代码调用系统gcc无需手动编译统一输出归一化normalize()函数移除每行末尾空格、合并连续空行模拟OJ判题的宽松比对逻辑超时保护防止死循环卡死5秒强制终止错误导向输出失败时同时打印期望值与实际值的repr()清晰显示换行符、空格等不可见字符差异。本地测试通过 ≠ OJ AC但它能帮你拦截掉80%以上的低级错误比如忘记printf(\n)、scanf读入格式错误、数组越界导致输出错乱。这是你提交前的最后一道防线。3. 1000–1099题典型解法模式拆解从输入处理到边界防御的6类高频陷阱杭电OJ 1000–1099题看似简单实则暗藏大量“约定俗成”的判题规则。官方不写文档全靠选手踩坑总结。我逐题跑通这100题后归纳出6类必须硬编码进你肌肉记忆的模式。它们不是“技巧”而是OJ判题机的真实行为逻辑。3.1 输入终结符陷阱EOF不是CtrlZ而是文件流自然结束几乎所有题都要求“多组输入直到文件结束”。新手常写// ❌ 错误写法依赖scanf返回值但未处理EOF while (scanf(%d %d, a, b) 2) { printf(%d\n, ab); }问题在于Windows下CtrlZ、Linux下CtrlD只是模拟EOF而OJ后台是把整个测试数据文件喂给你的程序。当scanf读到文件末尾时返回值是EOF-1不是0或2。正确写法必须显式检查EOF// ✅ 正确写法严格按EOF判断 while (scanf(%d %d, a, b) ! EOF) { printf(%d\n, ab); } // 或更健壮的写法兼容空行、多余空格 while (~scanf(%d %d, a, b)) { printf(%d\n, ab); }Python同理不能用try-except EOFError因为OJ输入是完整文件流# ✅ 正确写法用sys.stdin持续读取 import sys for line in sys.stdin: if not line.strip(): break a, b map(int, line.split()) print(a b)血泪经验1000题用while(scanf!EOF)能过但1089–1096系列题多组输入空行分隔若不处理空行必WA。OJ测试数据里常插空行scanf跳过空白字符但gets()或fgets()会读入空行——选哪个函数取决于题目是否要求“原样输出空行”。3.2 整数溢出防御1002大数AB必须用字符串模拟int64不够用1002题明确要求“A and B are positive integers, but may be very large.”。此时long long64位仍可能溢出如10^1810^182×10^18 2^63≈9×10^18看似够但题目说“very large”实测有1000位数字。必须用字符串模拟加法// 1002.c 关键片段字符串大数加法 void add_str(char *a, char *b, char *res) { int len_a strlen(a), len_b strlen(b); int i len_a-1, j len_b-1, k 0, carry 0; while (i 0 || j 0 || carry) { int sum carry; if (i 0) sum a[i--] - 0; if (j 0) sum b[j--] - 0; res[k] sum % 10 0; carry sum / 10; } res[k] \0; // 反转结果 for (int l 0; l k/2; l) { char t res[l]; res[l] res[k-1-l]; res[k-1-l] t; } }避坑点很多AC代码用__int128GCC扩展或Java的BigInteger但OJ服务器用的是标准GCC 4.8.2不支持__int128Java需注意Scanner读大数极慢应改用BufferedReader。3.3 输出格式零容忍1003 Max Sum的换行与空行是判题关键1003题要求“Output the maximum sum in one line. Output a blank line between two cases.”。这意味着每个Case输出后必须有一个空行最后一个Case后不能有多余空行空行必须是纯粹的\n不能是\r\nWindows换行。错误代码// ❌ 错误最后多了一个空行 for (int i 0; i n; i) { printf(%d\n\n, max_sum[i]); // 每次都输出两个\n }正确写法// ✅ 正确控制空行只在Case之间 for (int i 0; i n; i) { printf(%d\n, max_sum[i]); if (i n-1) printf(\n); // 最后一个Case后不输出空行 }Python同样要注意# ✅ 正确用print()而非print(\n) for i, s in enumerate(sums): print(s) if i len(sums) - 1: print() # 单独print()输出一个空行玄学现象某些题如1021 Fibonacci Again要求“Output a blank line after each test case”即每个Case后都要空行包括最后一个。务必逐字阅读题目Output描述OJ对换行的校验比对内容更严格。3.4 字符串处理边界1020 Encoding的连续字符计数不能漏掉末尾段1020题要求将aaabbbcc编码为a3b3c2。常见错误是循环到i len-1漏掉最后一段// ❌ 错误i从0到len-2末尾字符未处理 for (i 0; i len-1; i) { if (s[i] s[i1]) cnt; else { printf(%c%d, s[i], cnt); cnt 1; } } // 漏了最后一段正确写法必须包含循环外的收尾// ✅ 正确统一处理用ilen int i 0; while (i len) { char c s[i]; int cnt 0; while (i len s[i] c) { cnt; i; } printf(%c%d, c, cnt); }3.5 数学题精度陷阱1018 Big Number的斯特林公式必须用log避免溢出1018题求n!的位数。直接算n!再log10必然溢出。正确解法是斯特林公式近似$$ \log_{10}(n!) \approx \log_{10}(\sqrt{2\pi n}) n\log_{10}(n/e) $$但直接计算pow(10, ...)仍会溢出必须全程用log10// ✅ 正确所有运算在log域进行 double log10_n_fact(int n) { if (n 1) return 0.0; double pi acos(-1.0); return 0.5*log10(2*pi*n) n*log10(n/exp(1.0)); } int digits (int)floor(log10_n_fact(n)) 1;3.6 动态规划状态压缩1087 Super Jumping的O(n²)解法必须剪枝1087题是LIS最长上升子序列变种要求“strictly increasing”。朴素O(n²) DP// dp[i] 以i结尾的最大和 for (int i 0; i n; i) { dp[i] a[i]; for (int j 0; j i; j) { if (a[j] a[i]) { dp[i] max(dp[i], dp[j] a[i]); } } }但1000数据规模下O(n²)10⁶可接受。真正陷阱是初始化与边界dp[i]必须初始化为a[i]至少取自己不能初始化为0否则负数序列会出错。4. 常见问题排查1000–1099题提交失败的5个高频原因与现场诊断法即使代码逻辑正确OJ也常因环境细节拒绝你的提交。以下是我在100次WA/TLE/RE中总结的5条铁律每条都附带现场诊断命令无需登录OJ后台纯本地即可验证。4.1 “Compile Error”但本地编译成功检查OJ GCC版本与C标准现象本地gcc 11.2编译通过OJ报Compile Error: unknown type name bool。原因OJ服务器使用gcc 4.8.2默认C标准为gnu89不支持C99的stdbool.h和bool类型。诊断法在本地用OJ同版本编译# 安装gcc-4.8Ubuntu sudo apt install gcc-4.8 g-4.8 # 用-O2 -stdgnu89模拟OJ环境 gcc-4.8 -O2 -stdgnu89 1000.c -o 1000解决禁用C99特性用int代替bool手动定义true/false。4.2 “Time Limit Exceeded”但本地0ms检查输入/输出缓冲现象本地秒出结果OJ TLE。原因C语言未关闭stdout缓冲大量printf阻塞或Python未用sys.stdout.write替代print。诊断法用strace看系统调用strace -c ./1000 1000.in 21 | grep write若write调用次数远大于预期如1000次说明缓冲未生效。解决C中加setbuf(stdout, NULL)Python中用sys.stdout.write()sys.stdout.flush()。4.3 “Wrong Answer”但样例全过检查多组输入的初始化遗漏现象Sample Input/Output全对但OJ WA。原因全局变量或静态数组未在每组Case开始时重置。例如1010 Pairs of Songs的计数数组cnt[1001]若只在main开头清零第二组数据会残留上一组的值。诊断法在代码开头加调试输出printf(Case %d: cnt[0]%d\n, case_num, cnt[0]);提交前注释掉但本地测试时打开观察多组间变量状态。解决所有非const全局变量必须在while循环内初始化。4.4 “Runtime Error”无提示检查数组越界与栈溢出现象OJ只显示RE无崩溃信息。原因C数组声明过大如int a[1000000]在栈上分配或递归过深爆栈。诊断法用ulimit限制栈大小模拟ulimit -s 8192 # 设栈为8MBOJ典型值 ./1000 1000.in若本地也段错误则确认是栈溢出。解决大数组改用static int a[1000000]全局/静态存储区或malloc动态分配。4.5 “Presentation Error”检查行末空格与多余空行现象PEPresentation Error非WA。原因OJ比对输出时允许行首/行尾空格但不允许行间多余空行且最后一行不能有换行符。诊断法用hexdump看输出二进制./1000 1000.in | hexdump -C # 查看最后是否为0a\n以及是否有0d 0a\r\n解决C中printf(%d, ans)后不跟\n由主循环统一控制Python用print(ans, end)。注意PE不算WA但影响排名。杭电OJ的PE判定极其严格建议所有输出语句后加fflush(stdout)C或sys.stdout.flush()Python确保立即输出。5. 批量验证与代码管理用MakefileGit构建个人OJ训练流水线单题调试效率低百题需系统化管理。我用MakefileGit构建了一套“提交即验证”流水线让1000–1099题的训练像CI一样自动运转。5.1 目录结构标准化每个题目一个独立目录含代码/测试/配置hdu-1000-1099/ ├── Makefile # 全局构建入口 ├── config.json # 语言偏好、OJ账号可选 ├── 1000/ │ ├── 1000.c # C语言实现 │ ├── 1000.py # Python实现可选 │ ├── 1000.in # Sample Input │ ├── 1000.out # Sample Output │ └── README.md # 解题思路、坑点记录 ├── 1001/ │ ├── ...5.2 Makefile自动化一键编译、本地测试、远程提交、结果归档# Makefile .PHONY: all clean test submit archive # 从config.json读取配置简化版实际可用python生成 LANG_ID ? 5 HDU_USER ? your_username # 获取所有题目目录 PROBLEMS : $(shell find . -mindepth 1 -maxdepth 1 -type d -name [0-9]* | sort) # 对每个题目执行操作 define PROCESS_PROBLEM $(1)/$(notdir $(1)).out: $(1)/$(notdir $(1)).in $(1)/$(notdir $(1)).c echo Testing $(notdir $(1)) gcc -O2 -stdgnu89 $$(1)/$(notdir $$(1)).c -o $$(1)/a.out 2/dev/null || (echo Compile failed; exit 1) ./$$(1)/a.out $$(1)/$(notdir $$(1)).in $$(1)/tmp.out 2/dev/null || (echo Runtime error; exit 1) diff -w $$(1)/$(notdir $$(1)).out $$(1)/tmp.out /dev/null 21 echo ✓ Local test passed || (echo ✗ Local test failed; exit 1) rm -f $$(1)/a.out $$(1)/tmp.out $(1)/submit.log: $(1)/$(notdir $(1)).c echo Submitting $(notdir $(1)) python3 submit_hdu.py $(notdir $(1)) $(LANG_ID) $$(cat $$(1)/$(notdir $$(1)).c) $$(1)/submit.log 21 tail -n 3 $$(1)/submit.log endef # 为每个题目生成目标 $(foreach prob,$(PROBLEMS),$(eval $(call PROCESS_PROBLEM,$(prob)))) all: $(addsuffix /1000.out,$(PROBLEMS)) echo All local tests passed. test: $(addsuffix /1000.out,$(PROBLEMS)) submit: $(addsuffix /submit.log,$(PROBLEMS)) clean: rm -f */a.out */tmp.out */submit.log archive: tar -czf hdu-training-$(shell date %Y%m%d).tar.gz *.md */*.c */*.py */*.in */*.out执行make自动编译所有C题运行本地测试执行make submit对所有题调用submit_hdu.py结果存入*/submit.log执行make archive打包当前训练成果含代码、测试数据、日志。5.3 Git提交规范每次AC都是一次原子提交附带OJ判题截图我坚持每道题AC后立即Git提交message格式固定AC HDU-1000: AB Problem (C, 0ms, 204K)并附上submit.log内容与OJ网页AC截图存为1000/ac-screenshot.png。这样做的好处是回溯时git log --oneline就是你的AC进度条git blame 1000.c能看到哪行代码修复了哪个WA某天发现1000题又WA了git checkout回退到AC版本对比差异快速定位新引入的bug。后悔药实践曾因修改全局宏#define MAXN 1000为10000导致1001题数组越界。用git diff HEAD~1 1001.c三秒定位问题比重读代码快十倍。这套流水线不追求“全自动AC”而是把人的思考过程固化为可追溯、可复现、可回滚的操作链。当你在1099题卡住三天翻看git log里1000–1098的每一次提交会发现那些曾经让你抓狂的边界条件、输入格式、输出空行早已被你的commit message和测试文件默默记录下来——这才是1000–1099题真正的价值不是一百个答案而是一百次把模糊直觉锤炼成确定性工程习惯的过程。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进