非传统题与随机算法
这类通信题一般指,仅进行一次通信的通信题,这也是大多数通信题的形式。
一种通信策略是对信息的(有损)压缩,你可以在保证一定正确率的情况下只传递部分信息。
另一种策略是将信息转化成另一种形式,比如哈希。
在实际问题中,需要关注对信息的需求,分析哪些信息是冗余的,这往往是解决通信题的关键。
https://qoj.ac/contest/997/problem/4675
题意:
给定两个长度为
C 要根据收到的信息回答
解法:
考虑一下有损压缩,我们从
看起来这样很可行,实际上如果这
再考虑一下哈希,一个二进制数可以看成一个集合,异或运算可以看成集合的对称差运算,而对集合的哈希可以考虑 xor-hashing,即,对每一个数
由于异或存在消去律,令两个集合
回到题目中,我们将题目里的所有二进制数看成集合,异或看成对称差运算,A 和 B 共用同一个
https://qoj.ac/contest/1271/problem/6669
题意:
A 有
B 在收到信息后,需要回答
解法:
直觉上特别违背信息论,因为只能传递
由于 B 在回答问题的时候知道了
先考虑有损压缩,尝试随机删掉所有
但是我们的目标是满分,我们不能在
假设 B 知道了所有
拉格朗日插值!
https://qoj.ac/contest/878/problem/3098
题意:
给定一个长度为 X
,Y
,Z
的字符串,你可以不断的删去一个字符,如果选择了一个 Y
,且它左边是 X
,右边是 Z
,你会得
A 和 B 都知道
解法:
按照这节的引言所说,首先我们需要明确 B 需要什么,不妨简化一下问题,如果 B 知道了所有信息怎么做。
将最左边的 X
和最右边的 Z
拿出来,只有中间那一段有用。对于一个同字符的极长连续段,其中也只有一个有用。也就是说字符串一定可以被转化成 XY?Y?Y?...?YZ
的形式。观察到其中一定存在子串 XYZ
,将这个 Y
删去,然后删去两边之一(注意不要把头尾删去),就能得到一个子问题。
一个特别简洁的方法是,取最左边的 Z
,然后从这个 Z
前面的位置不断往前删,直到前面只剩下一个开头 X
(注意这个 X
不要删掉),然后删去这个 Z
,取下一个 Z
进行相同的步骤。这个做法只需要知道所有 Z
的位置。进一步观察,发现只需要知道每一个 Z
的连续段的最后一个位置就行。
也就是说,我们发送的长度为
将串每
至于如何实现?我们可以计算出一个串是字典序第
- https://yundouxueyuan.com/p/YDSP2023D1BonusB
- https://qoj.ac/contest/1782/problem/9237
- https://qoj.ac/contest/1633/problem/8642
与
大多数情况下,一方充当“交互库”的角色,解决问题的关键是如何设计另一方的问题,即,我们首先需要明确另一方需要什么,然后问题就转化成了
下面这个例题中,双方互相充当对方的“交互库”。
题意:
A 手上有一张
A 想知道,若考虑所有
解法:
考虑 Dijkstra 的过程,每次找到一个距离最小的点去松弛,如何知道最小的点?我们可以令 A 和 B 把最小的距离发给对面,如果发现自己的距离小,就将对应点的编号发给对面,这样双方都知道最小的点了。
还有一个问题是,这个距离可能会达到
有些题目交互库是自适应的,也就是说 A 和 B 的通信,会被很“聪明”的交互库干扰,一般来说这个交互库真的很聪明,以至于在假设交互库的策略均匀随机的情况下,即使有着极高的正确率,依然无法通过。
但是,你不让交互库看出来你在说什么,不就行了吗?😂
https://qoj.ac/contest/1684/problem/8726
题意:
A 和 B 参与一个游戏,他们被关在两个房间中,不能相互通信,游戏的步骤如下:
- A 选择一个整数
告诉主持人 C。 - C 告诉 A 一个整数
。 - A 生成一个
个节点的有标号的无向无根树,告诉 C。 - C 从树中删去至多
条边,并将剩余的边告诉 B。 - B 根据这棵残缺的数猜出
。
解法:
我们不妨传递一条链,A 和 B 事先商量好一个随机排列
在 C 的视角中,他收到的信息可以近似的看成一个随机排列,他不知道在说什么,只能闭眼乱删一半的边。
取
B 接收到信息之后,只需要看每一个二进制位,是否存在至少一个 II 类点相邻,即可确定这一位是什么。
这题还有很多离谱做法,比如把节点
或者,对于所有点,如果代表的数位是
😅。
- 尝试用这种方法在 习题 1.1 的一道题 https://qoj.ac/contest/1782/problem/9237 中得到尽可能高的分数。
一般来说做交互题时,你需要先明确解决问题需要什么信息,针对这一点设计询问方式。
一些题目需要运用启发式算法(俗称乱搞),还有一些题目需要发现一些性质,或者“灵光一现”。
这部分的题目,解法大多为随机化算法或启发式算法。
对于复杂的随机化算法或启发式算法,只需要感性理解复杂度,不需要证明,多数情况下,不断加入你所认为正确的优化,优化着优化着,就过了。
题意:
给定整数
你可以进行至多
- 给每个节点
设定一个权值 。 - 选取一个边的子集
。 - 对于每个
,交互库会计算出 ,即,在只考虑 中的边的情况下,所有 的邻居的权值异或和。 - 你会得知所有的
。
解法:
将所有边随机分成大小相等的
如果是一棵树的话,可以类似拓扑排序,逐步删叶子(叶子的判定只需要检查
题意:
给定一个
你可以进行若干次询问,每次询问给出两个点集的子集
试通过询问求出一条最长路。
解法:
考虑充分利用“每三个点之间至少有一条边”的性质。
维护两条链
- 检查
之间是否有边,如果有就将 接在 后面,然后清空 。 - 任取一点
,检查 之间是否有边 ,如果有就将 加入 末尾。如果没有,此时要么 为空,可以把 加到 的末尾;要么 没边, 没边, 一定有边,也可以把 加到 的末尾。
执行上述步骤,会得到两条链,查询
询问次数是
维护两条链,可以设计势能函数为:两条链大小之和。检查
如果同时维护多条链,每次随机取两条链,检查末尾是否有边,可以设计势能函数为:链的个数。检查末尾是否有边这一操作,有概率减少势能。
还有一个优化是,有时会已知存在两条链的末尾之间没有边,此时不需要检查,直接拿来做即可。
https://yundouxueyuan.com/p/YDRG005F?tid=65ccf4cbfbfb0fad8af18874
题意:
本题的麻将不含字牌,共
在本题中,你需要和交互库玩如下游戏:
- 游戏开始时,交互库将这一副麻将按某种顺序排列组成牌堆,保证牌堆顶
张牌的听牌集合大小 。 - 交互库将牌堆顶
张牌作为她的手牌,然后将牌堆顶第 张到第 张牌(共 张牌)亮出,你可以看到这些牌,注意这些牌可能属于交互库手牌的听牌集合,也可能不属于。然后移除这 张牌(即此时牌堆顶的牌,是初始牌堆的牌堆顶第 张牌)。 - 你猜测一张牌,若这张牌属于交互库手牌的听牌集合,则游戏结束;否则交互库会亮出牌堆顶的
张牌中不属于听牌集合的所有牌,并移除牌堆顶的 张牌,你需要继续猜测。注意,如果听牌集合内的一张牌全部被翻出,其仍然属于听牌集合。 - 游戏结束时,令你的代价为你的猜测次数。
你需要尽可能最小化游戏的代价。
保证数据随机,
解法:
这是一道考察纯启发式算法的交互题。
如果从
本题中保证至少两面听,大致可分为如下几种听牌的类型:
型,例如1m 1m 1m 2m 2m 2m 3m 3m 3m 4m 4m 2s 3s
。 型,例如1m 1m 1m 2m 2m 2m 3m 3m 3m 1s 2s 3s 4s
。- “双碰”型,例如
1m 1m 1m 2m 2m 2m 3m 3m 3m 4s 4s 5s 5s
。
也可能会有
介绍一下麻将的防守技巧:
- 筋牌:假设你知道对面不听某花色的
,那么听 的概率会大大降低(不存在 或 型),听 的概率会有所降低。 - 壁牌:假设牌河中已经有很多某花色的
,那么听 的概率会大大降低;若牌河中有很多 ,那么听 的概率会大大降低。
上述技巧不能适用于“双碰”型,即
我们需要对筋和壁设计估价函数,如果对这两种防守技巧设定参数,会变得很复杂,尤其是壁牌。但这两种技巧有一个共性,即它们都减少了在剩余牌中凑出听牌形状的方案数。
我们令
这个函数的表现并不是很理想,我们令
欢迎各路大神薄纱 std!
- https://qoj.ac/problem/9909
- https://qoj.ac/problem/9914
- https://qoj.ac/problem/9156
- https://qoj.ac/problem/5568
- https://uoj.ac/problem/810
这一节中的题目,往往需要多发现性质,或者需要“灵光一现”。
https://qoj.ac/contest/1300/problem/6774
题意:
你需要猜测一个长度为
交互库会做如下的过程,初始时令
操作完后会告诉你
也就是说,你需要给出一个大小为
你至多进行
解法:
令
由于不需要优化询问次数,且询问次数限制为
一个想法是选一些位置,问出它们的异或和。询问的自动机大概是有左右两部分,读取到这些位置的时候,如果这个位置为
搞两个长度为
不幸的是,这个矩阵不满秩,如果想要满秩的话,
考虑先确定前
然后考虑确定后
对于中间
用 bitset 做消元,时间复杂度
常用的方法有:iddfs,A*,idA*,模拟退火,爬山,随机调整,估价函数。
但这不意味着只要了解了这些算法,你就是提答高手了。对什么设计 A* 的估价函数,怎样剪枝,对什么退火,如何随机,怎样实现更高效,都是要慢慢摸索的。
此类题目的关键是找一个好的估价函数。
好的估价函数往往需要一定的时间尝试,“提答花的时间越久分数越多”这句话的原因就在此。
题意:
用尽可能少的步数求解数字华容道。
题解:
对于测试点
对于测试点 可以剪十六个小纸片玩,观察发现前若干次操作一定是转矩形的最外面一圈,类似于 UUULLLDDDRRRUUULLLDDDRRR...
的过程,当把
考虑 A*,设计一个估价函数,每一层只保留一些可能比较优的状态,可以试试如下几个。
- 每个数到终点的曼哈顿距离,的和/平方和/立方和/平方根和。
- 重新定义距离:
的距离为 ,算和/平方和/立方和/平方根和。 - 将距离乘上
到这个数的距离,算和/平方和/立方和/平方根和。
实测和/平方根和比较优秀。
这样子会出现一个现象,目前队列中估价函数最低的会在两个数中反复横跳,每次扰动一步显然是不够的。可以在模
需要调一调删除的频率和保留的状态数。
当然估价函数并不全面,比如有两个相邻但位置相反的数,空格又隔得特别远。其实这种状态是很劣的,但这几种估价都会认为这种状态很优秀。或者说多个路径交错在一起,也是特别劣的,但很难设计函数把它们区分开,欢迎各位来交流更好的估价函数。
题外话:关于多项式复杂度的构造方法,也是我的赛时做法:
- 大概思路是按照
的顺序归位,已经归位的不去动它。 - 对于前
行的前 列,记录状态 ,表示目前需要归位的数和空格分别在什么位置,爆搜就行。 - 对于前
行每一行的最后两列,并不能保证上一条能跑出解,但是我们一定可以把棋盘变成下面的形状之一(.
表示空格,?
表示没被归位的数字):
1 2 . 3
? ? ? 4
1 2 4 .
? ? 3 ?
所以记录状态时记录两个数的位置和空格的位置,也一定能得到解。
- 对于后两行,由于最后一行极有可能顺序不对,上述方法不能使用。从左往右枚举列,同时归位这一列的两个数,假设
,目前归位 ,只需要在第三行令 和 挨在一起,然后转下来就行,也可以记录三个坐标爆搜。
1 2 3 4
5 6 7 8
? 13 9 ?
? ? ? .
复杂度
但这样构造,我只在测试点
与
要注意的是,一般来说,跑一轮退火容易陷入局部最优解,跑多轮退火就能解决这一点。
题意:
给一棵无向无权树,你需要往里面加入
解法:
说一下我做这个题的过程:
我一开始想的是,加入一条令总距离减少最多的边,但这样实在是太慢了,于是我就随了几条边取最优,这个表现并不是很优秀,在 case 2 只得到了
打算给 case 1 写一个暴力,算量是
直觉告诉我,菊花的根选择重心,但是选择菊花叶子比较麻烦,因为算一次代价是
题目中的代价不好维护,我想了一个估价函数,我们不妨忽略菊花,直接把菊花中的所有点“缩起来”。由此想到估价函数:每个点到菊花根的最短路径之和,这样计算一次代价只需要
先贪心选令估价函数减少最多的点,选出一个集合,枚举集合内哪个作为根,可以在
对叶子退火试试?每次扰动选择一个叶子和菊花图外的点交换并估价,每个点跑五六分钟就可以得到满分。
此题还有别的做法,一种是对叶子爬山,每次扰动随机一个子集换出,这个子集大小需要调一调,你甚至可以动态调整,发现爬不动了就多换一点。
还有一种是最小化估价函数的确定性做法,类似 NOI2008 奥运物流的二维树上背包,实现的好可以在一秒内出解,很厉害。
我的退火代码在 i7-1260P 2.10GHz 16GB
上,能在一分钟的时间对 case 6 处理完毕。
熟悉笔者的都知道,笔者是非传统题和启发式算法的究极爱好者,热衷于用各种“歪解”过题。
比如 2023 NOIP t3 ,我在长时间的坐牢之后,突发奇想用特殊性质的代码去跑没有特殊性质的大样例,发现只筛掉了约一半的测试点,由此尝试把序列 reverse 一下再跑一遍,就通过了;在 2024 联合省选 d2t2 中,对贝尔数的搜索加了很多剪枝,多跑过了
到了 NOI 2024,我非常幸运的在 day1 遇到两道擅长的题目:t1 是本文开头提到的 xor-hashing,t2 是我擅长的交互题,这两道题都是飞速通过,给 t3 留下了充足的时间;在 d2t1 中,根据多年写搜索的经验,想到去优化搜索的最后一层递归,多获得了
进队之后,我被邀请到一些学校讲课交流,在正式内容提前讲完的情况下,我都会用之前做的非传统题填满剩下的时间。如果一切顺利,这篇文章会是我的候选队论文,可惜我在 CTT 中惨烈坠机,准确的说,根本就没起飞,没有任何一场比赛进入过前
每当有人和我提起“乱搞无用论”之时,我都会用我的 oi 生涯坚定的反驳他,如果我不会乱搞,这个集训队我还能进吗,甚至说,这个江苏省队我还能进吗?
希望你们都爱上乱搞,爱上非传统题!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现