在信息学竞赛的圈子里,流传着一本著名的神作《骗分导论》。所谓的“骗分”,不是指作弊或违反考场规则,而是在面对自己无法写出完美正解的题目时,利用竞赛的评测机制、数据范围和题目特性,尽可能多地搜刮“部分分”的算法艺术。
CSP-J/S 的评测机制是按测试点给分。这意味着:即使你不会写 的满分正解,只要你能用各种手段拿到 30 分、50 分甚至 80 分,积少成多,也能轻松锁定一等奖!
本文将为你系统梳理 CSP-J/S 考场上合法且极其高效的 “骗分”技巧全攻略。
一、骗分基本功:吃透 Subtask 与分类拼凑
CSP 题目的数据范围表格中,通常会将测试点拆分为若干个小块(Subtask)。这就是官方送给你的“骗分路线图”。
1. 暴力(DFS / 暴力枚举)打底
任何题目,拿到手如果 20 分钟内没有正解思路,第一件事就是写暴力。
- 全排列枚举。
- 子集折半或 DFS 爆搜。
- 或 多重循环。
原则:不要嫌弃暴力只有 20~30 分。四道题的暴力全部拿到,往往就已经接近一等奖分数线了!
2. 针对“特殊性质”单独写逻辑
题目说明里经常包含类似这样的字眼:
- “另外 20% 的数据,满足树退化为一条链” 当作一维数组处理。
- “另外 20% 的数据,保证 ” 无需考虑负数,直接贪心。
- “另外 15% 的数据,满足 ” 特殊单次询问,推导简单公式。
3. 用 if-else 拼凑得分(分段得分法)
把不同 Subtask 的代码写在不同的函数里,在 main 函数中用 if-else 分流。这是最标准的“拼分”架构:
#include <bits/stdc++.h>using namespace std;
int n, m;
bool is_chain() { // 检查输入数据是否符合“链”的特征 return true;}
void solve_dfs() { /* 20% 分数:暴力爆搜 */ }void solve_chain() { /* 20% 分数:链的特殊性质 */ }void solve_k1() { /* 20% 分数:K=1 的情况 */ }void solve_rand() { /* 剩余数据:随机化 / 贪心 */ }
int main() { cin >> n >> m; if (n <= 10) { solve_dfs(); } else if (is_chain()) { solve_chain(); } else if (m == 1) { solve_k1(); } else { solve_rand(); // 拿不到全分,撞大运拿几分 } return 0;}二、进阶骗分:启发式搜索与卡时技巧
如果你只能写出暴力,如何让暴力程序跑得更快,从而“跨越”数据范围,骗到更多分数?
1. “卡时”大法(Time-based Exit)
评测机单点限时通常为 1.0 秒()。如果你的搜索或随机算法可能会 TLE(超时),可以利用 <chrono> 或 clock() 在超时前强行终止并输出当前找到的最优解!
#include <iostream>#include <chrono>
using namespace std;
auto start_time = chrono::steady_clock::now();
bool time_out() { auto now = chrono::steady_clock::now(); double elapsed = chrono::duration<double>(now - start_time).count(); return elapsed > 0.92; // 超过 0.92 秒立刻停止,留 0.08 秒做收尾和输出}
void dfs(int state) { if (time_out()) return; // 随时检查时间 // 正常的 DFS 逻辑...}2. 贪心 + 随机打乱(Random Shuffle)
在图论、背包、NP 难问题中,单纯的贪心可能会被构造数据卡掉。但是:“贪心不够,随机来凑”!
我们可以将输入数据的顺序随机打乱(std::shuffle),然后运行 100 次贪心,取所有运行结果中的最优值:
int best_ans = 0;while (!time_out()) { // 在限时内反复跑 shuffle(a.begin(), a.end(), rnd); // 打乱输入顺序 int current_ans = greedy_solve(); best_ans = max(best_ans, current_sum);}cout << best_ans << endl;真实战绩:这种“随机化贪心”在许多复杂的求解/优化题中,经常能把 30 分的暴力骗到 70~80 分甚至奇迹 AC!
3. 模拟退火(Simulated Annealing)与爬山法
对于求解极值(最大值/最小值)的几何、图论或组合优化问题,如果在考场上想不到 DP 或高级数据结构,直接写一个模拟退火模板。调好参数后,大概率能刷过大半测试点。
三、奇技淫巧:打表、猜输出与特例骗分
1. 本地打表(Precomputation)
当题目要求输出某个特定数学序列或小范围状态(例如 的某种方案数),但你的算法运行一次需要 10 秒(本地能跑,提交会 TLE):
- 在本地电脑上运行你的暴力程序,把 的答案全部算出来。
- 复制这些结果,在代码里硬编码写成一个全局数组:
int ans[] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...};int main() {int n; cin >> n;cout << ans[n] << endl; // O(1) 瞬间输出}
2. 猜测特例输出(无脑偏分)
当题目时间所剩无几(比如只剩最后 3 分钟),实在写不出任何代码时:
- 如果题目问“是否存在解,不存在输出 -1”:直接全输出
-1。评测数据中往往有 10%~20% 的点是没有解的! - 如果题目问“YES 或 NO”:全输出
YES或全输出NO(通常能拿到 10%~30% 的分数)。 - 如果题目求最小值/最大值:输出
0或1或题目中给定的某个边界极值。
3. 样例反推(打表找规律)
如果题目看起来像数学题或博弈论,先写个小暴力把 的输出答案打出来,观察数列:
- 是不是等差/等比数列?
- 是不是斐波那契数列?
- 是不是
N % 4 == 0时先手必胜? 很多时候,甚至不需要严谨的数学证明,找出了规律,写出单行公式就能直接 AC!
四、骗分的底线与避坑指南
骗分虽然快乐,但必须在规则允许的范围内进行。以下是考场绝不能触碰的红线:
- 严禁攻击评测机/读取测试数据:
- 不要尝试读取根目录或系统文件,不要试图打开同目录下的
.in文件(除非是题目的freopen),这会被系统判定为违规作弊,取消成绩!
- 不要尝试读取根目录或系统文件,不要试图打开同目录下的
- 警惕 RE(运行错误)导致 0 分:
- 骗分代码如果写得太粗糙,可能会导致数组越界或除以零。遇到 RE,该测试点直接 0 分。写暴力时数组同样要开够。
- 不要让“骗分”影响了“正解”:
- 如果你在某道题上有 80% 的把握写出正解,请优先写正解!“骗分”是在正解无望情况下的退而求其次,切勿本末倒置。
五、总结:骗分的终极心法
信息学竞赛不是满分通关游戏,而是一场分数的争夺战。
- T1 / T2:追求稳拿正解(100 + 100)。
- T3 / T4:正解不会写?暴力 30 分 + 特殊性质 20 分 + 随机化/卡时 20 分 = 70 分。
当你在考场上学会了把每道题的“渣渣分”搜刮干净,你会发现一等奖的门槛其实并没有想象中那么高。祝你在 CSP-J/S 考场上逢考必过,分分必争!