基于 hack 的赛制的设想
Part 1 引入
你会不会觉得传统的比赛有些无聊,反正我是这么觉得。传统比赛缺少趣味性,并且让个人发挥的空间十分的少,个人(团队)之间的竞争也比较少。
我的想法是创造一个新赛制,我称它叫 HC(Hack Code)。通过字面可以知道这个赛制是有hack(类似于codeforces的hack)的。
Part 2 基础规则介绍
HC赛制应该是一类赛制,以下先讲最基础的规则,然后再讲在基础规则上的一些变化。
Part 2.1 比赛时间分配与题目
一场比赛通常由两人(或两队)对抗,并且只有一道题。比赛分为两个时间段,第一个时间段是 code 时间(2小时),第二个时间段是 hack 时间(3小时)。
在比赛时每道题双方都要投入极大时间(下文会讲),如果比赛有多道题,那么解决一道题的时间就不足了,所以只设了一道题。
字面意思,code 时间就是双方对这道题写出相应的代码,hack 时间则是要看对方的代码,弄出相应的输入数据使对方的代码运行速度变慢。
Part 2.2 评判机制
双方可以多次 hack,每次 hack 都有一个 \(T\) 值,最后取 \(T\) 的最大值为最终 \(T\) 值,双方谁的最终 \(T\) 值小谁将获胜。
其中一次 hack 的 \(T\) 值通过程序运行时间和hack时比赛经过的时间计算,分别为 \(S\) 和 \(M\)。
Part 2.2.1 程序运行时间S
程序运行时间的测量单位越小越好(比如ms),假设此次 hack 程序运行了 \(p\) 个单位时间。
\(k\) 是一个系数,上面的 \(max_p\) 是这道题的时间限制。
这里的S为什么不用线性的公式,而是用 \(\log\) 的呢?
举一个例子:有两个选手比赛,分别为 A 和 B,并且此时用线性公式。假如 A 代码运行时间为1ms, B 代码运行时间 2ms,可以看出 A 与 B 代码速度是差很多的,A 是 B 的一半。但是如果后面的 \(M\) 的值相差一点,B 的 \(T\) 就可能比 A 的小。
上面的可能不理解,但是如果数据范围大一些,A 同样是 B 的一半,A 代码运行时间为 100ms,B代码运行时间为 200ms,M就得差很多才能让两者的 \(T\) 值差开。所以我们用 \(\log\) 计算。
Part 2.2.2 hack时比赛经过的时间M
M的值需要是递减的,并且值不能太大,\(s\) 为 hack 时比赛经过时间。
\(max_s\) 是 hack 的总时长,这里的 \(j\) 是一个系数,它需要比\(k\) 小很多,比如 \(j = \frac{k}{10}\)。
Part 2.2.3 特殊情况与系数
当然我们还是会遇到一些特殊情况,比如运行时错误,答案错误,超时。这时的代码可以统一视为错误,\(S\) 的值就应该特殊计算。
此处这么算是为了防止一份正确的代码没一份错误的代码 \(T\) 值小。
系数中的 \(k\) 和 \(j\) 可以分别设定为10和1。
Part 2.3 代码限制
选手可能会为了让对手 hack 难度增加,会弄一些奇怪的方法。
比如增长代码长度,减小可读性。我们可以设置规则,比如代码长度不得超过 3KB,3KB 可能有点小了,当然还是因题而设吧。
有时双方用的语言不一样,或者对方的语言自己不懂,那就无法就行 Hack,所以我们要统一比赛语言,保证只有一个。但同一种语言也会出现问题,比如比较少见的函数或库,那就要限制库的使用。
Part 3 规则变化
Part 3.1 多时段
上面我们是把时间分为两个部分,只能 hack 一份代码,并且想逆风翻盘也很难。我们可以尝试把时间分为四个时间段,每个时间段有 1.5 小时,总共 6 小时。如下图所示。
其中只有第1,2,3时段是 code 的,只有第2,3,4时段是 hack 的,每次 hack 都是 hack 上一个时间段的代码。
这种分为多个时段,对于选手的任务量特别大,特别是第2,3时段,又要 code 又要 hack。所以这种多时段的比赛需要两支队伍比赛,而不能是两个人。
Part 3.2 多方对抗
上文所说的都是双方之间的对抗,这里会是多方之间的对抗,说直白点就是你需要 hack 好几份分别来自不同队伍的代码。
这里有两项原则,分别是对手佚名和不同对手。
对手佚名的意思是你不知道对手是谁,这可以保证你们之间不会有收买关系,或者双方都不互相 hack 的行为。这有利于保证比赛的公平性。
不同对手的意思是,假如你是 A,你的对手中有一个 B,不能存在一个 C 是 A、B 的共同对手。如果存在了 C,就会影响公平性,比如 C 很厉害,一开始它的 \(T\) 值最小,然后 A、B 就想合作 hack C选手的代码,此时 C 选手的 \(T\) 值就会变大,C 就输了。为了避免这种情况出现,我们要禁止存在对抗关系的双方有共同对手。
当然 Hack 多份代码是很困难,所以这需要队伍参赛,不能分为多时段,对手数量也不能太多。当然会有特殊的,比如题目十分简单,这时就可以让个人参赛,因为 hack 会变得比较容易。
Part 3.2.1 匹配机制
可以看出需要多人对抗,那前提得是参赛的人足够多,一般这是网络赛或者大型线下公开赛,那这就需要匹配机制。
匹配当然要合理,要有随机性和实力相近性。匹配前首先要有对每个人的 rating(参考elo rating)。根据每个人本身的 rating,计算出随机化的 rating。
\(R\) 是原来的 rating,rand 返回的是整数,且等概率。\(k\) 代表随机的极端化,\(k\) 越大,就越有可能出现 \(R_{now}\) 与 \(R\) 相差很大的局面。
\(W\) 是随机化程度,根据不同难度的比赛设定,\(W\) 的取值最好在 50 和 150 之间。
之所以要用 \(\log\),就是为了让参赛者有机会碰到 rating 较强(弱)且保证这种情况不是太多。
当然如果是大型线下赛,就无法获取选手的rating,此时的 \(R_{now}\) 就只能设置成完全随机。
(1) 对手数量为 1。需保证总参赛人数为偶数。对每个人的 \(R_{now}\) 排序成链,按照“选手1 与 选手2,……,选手 \(n-1\) 与 选手\(n\)”的方式分组。
(2) 对手数量为 2。可以把选手随机分为两部分,对这两部分排序成链,然后首与首相连,尾与尾相连,形成一个环,与该选手相邻的就是他的对手。
(3) 对手数量为 3。需保证总参赛人数为偶数,把选手分为数量相等的两部分,按照上面的方法把两部分变成两个环,然后让两个环同等位置的点连边。
Part 4 HC赛制的优点与发展
Part 4.1 发挥广
传统比赛的题目一般只有一个正解,哪怕有多个,要么和正解差不离,要么很麻烦。而HC赛制中,你一开始会想到大部分都会想到的正解,在优化时你可以往自己擅长的方向优化。相对于传统赛制而言,它对选手的限制不会很多。
Part 4.2 出题
看完上面讲到的,你可能会猜测这是一个卡常的比赛。没错,这就是一个卡常的比赛。但是卡常也有不同,可以分为代码细节上的卡常和思路上的卡常,代码细节上的卡常对选手实力考察没有多大关系,并且对选手没有提供什么实质上的经验。
比如下面的两段代码。
//第一段代码
int a;
long long b;
scanf("%d%lld", &a, &b);
printf("%lld\n", a + b);
//第二段代码
long long a, b;
scanf("%lld%lld", &a, &b);
printf("%lld\n", a + b);
我想应该大多数人都不知道哪段代码更快,这就是代码细节上的卡常。分析这两段代码没什么意义,哪怕它们之间速度有差距,那也没有多大。
我们需要避免这种代码细节上的优化。想要避免,前提是思路上能够卡常,并且思路上的卡常比代码细节上的卡常更优才行。这时我们需要对题目加以限制:
- 正解的实现不能过于简单。
- 不能让解法的优化有限制,也就是说在比赛结束前需要一直有可优化的地方。