周作业 ×
玄学搜索剪枝大赏 √
题意
给定 (x) 和 (y),每次有三种操作 (x:=x-1),(x:=x+1) 和 (x:=x imes 2),需要保证 (x geq 1)。
求 (x) 至少要几步才能变成 (y)。只能修改 (x)。
(1 leq x,y leq 10^5)
题解
BFS 即可。考虑剪枝掉 (x > 10^5) 的状态,这样保证了复杂度。
证明:如果我们需要让 (x) 乘上 (2) 后大于 (10^5),然后通过连续地减去 (1) 使 (x) 等于 (y)。设当 (x>10^5) 后需要减去 (p) 次才能到达 (y),显然可以在执行乘 (2) 操作前将 (x) 减去 (dfrac p2) ,后者的总操作次数比前者更优。
所以一定存在一个最优解,该解中的 (x) 在任意时刻均 (leq 10^5)。
题意
输入正整数 (n),把整数 (1,2,dots ,n) 组成一个环,使得相邻两个整数之和均为素数。输出时,从整数 (1) 开始逆时针排列。同一个环恰好输出一次。
(n leq 16)
题解
DFS 模板。值得一提的是,需要在搜索时剪枝掉当前所有不合法的状态,不能确定了环上每一个数之后再检查,这样复杂度会卡满 (O(n!)),没有任何通过的机会。
题意
求出所有 (0,1,2,...,9) 的排列 (a,b,c,d,e,f,g,h,i,j),使得
允许出现前导 (0)。
(2 leq n leq 79)
题解
DFS 模板题。因为 (10!=3628800) 并不大,所以我们可以枚举所有排列,判断其是否合法。
题意
给定长为 (n) 的整数数组 (S),求出其连续子序列中乘积最大的,并输出其乘积。连续子序列可以为空,此时乘积为 (0)。
(-10 leq S_i leq 10,1 leq n leq 18)
题解
显然可以枚举连续子序列的起点和终点,计算即可。
答案最大为 (10^{18}),需要使用 (64) 位整形存储。
题意
给定正整数 (k),求所有满足
且满足 (x geq y) 的正整数对 ((x,y))。
(1 leq k leq 10^4)
题解
考虑推式子。
即:枚举 (y(y in [k+1,2k])),看 (x) 是否为整数即可。
当 (y < k),(dfrac 1ygeq k);当 (y>2k),因为 (x geq y),(dfrac 1x+dfrac 1y<dfrac 1k),显然无法取到正整数解。
题意
如果一个字符串不包含两个相邻且相等的重复子串,那么它被称之为困难的串。
形式上来讲,对于字符串 (S(|S|=n)),如果不存在 (1 leq l leq left lfloor dfrac n2 ight floor) 和 (2l leq x leq n) 使得 (S_{x-2l+1...x-l}=S_{x-l+1...x}),那么 (S) 被称作困难的串。
求字典序第 (n) 小,且只包含前 (L) 个大写字母的字符串。
(n > 0, L leq 26)
题解
考虑 DFS 剪枝。对于字符串 (S(|S=n|)),当我们加入 (S_{n+1}) 时,只需要考虑第二个重复子串的末尾为 (S_{n+1}) 的相邻重复子串,因为前面的相邻重复子串如果存在,便已经在之前剪枝掉了。
题意
给定 (n imes n) 的国际象棋棋盘,求出骑士从某个点走到另外一个点的最小步数。
(4 leq n leq 300)
题解
BFS 即可。
双向 BFS 的正确做法是:起点出发的待扩展点存储在队列 (Q_1),终点出发的存储在队列 (Q_2),如果 (|Q_1|<|Q_2|),拓展一层 (Q_1),否则拓展一层 (Q_2)。
然而我的做法:将起点终点标记上不同的颜色,扔到队列里跑。这优化了个京华啊(捂脸)。
题意
给定容量为 (a,b,c) 升的三个无刻度杯子,最初只有第三杯装满了水。求至少需要倒多少水才能使某个杯子恰有 (d) 升水。
如果不可能,找到最大的 (d'<d),使得可以使某个杯子恰有 (d') 升水,并求出至少需要倒多少水才能使某个杯子恰有 (d') 升水。
(0 leq a,b,c,dleq 200)
题解
直接搜索状态太多((200^3=8cdot 10^6)),因为有多组数据,所以复杂度无法接受。
观察到三个杯子水量总和一定,记录前两个杯子里的水量即可求出第三个杯子的水。这样就只有 (40000) 个状态,可以接受。
题意
给定 (n) 个点的无向图。将这些点排列,得到 (p_1,p_2,...,p_n)。定义 (p) 的带宽为图中边的端点在排列里的最大距离。求带宽最小的排列。
(1 leq nleq 8)
题解
枚举全排列即可。
介绍 STL 函数: std::next_permutation(*start,*end)
如果存在下一个排列,返回非零值并将 [start,end) 转换为下一个排列。否则返回 (0)。
题意
有 (s) 个挂坠,重量为 (w_1,w_2,...,w_s)。有一些长度为 (1) 的木条,木条的每一端可以挂上挂坠,还可以连接另一根木条,但不能有某一端不放任何东西。木条长度不计。
你需要设计一种方案,使得能挂上所有的挂坠,且平衡(设左右侧到支点长分别为 (m,n),重量为 (a,b),那么必须满足 (ma=nb))。你需要使这个方案在长度不超过 (r) 的前提下长度最大。
(0 <r<10,1leq s leq 6,w_i leq 1000)
题解
根据题意,先搜索出所有挂东西的方案,然后我们调整支点的位置,来使该方案平衡,并计算出该方案的总长度。
容易观察到,任何方案可以转化为一棵二叉树,叶节点是挂坠。木条可以看作一条边,木条的端点为非叶节点。
具体来讲,记录该方案的最左侧点 (v_l) 和最右侧点 (v_r)。设根节点的位置为 (0),如果某个点在根节点左侧,则其位置为负,否则其位置为正。自顶向下考虑,对于每个非叶节点,设其左侧的总重量为 (w_l),右侧的总重量为 (w_r),那么左右两侧到该点的距离分别为 (dfrac{w_r}{w_l+w_r},dfrac{w_l}{w_l+w_r})。注意,某一侧偏重的时候,它到该点的距离近,这很容易被没有学过物理的人所混淆。那么记录该点的位置 (mid),则左右两侧的位置分别为 (mid-dfrac{w_r}{w_l+w_r},mid+dfrac{w_l}{w_l+w_r})。可以通过简单的取最小值 / 最大值来维护 (v_l,v_r)。最后 (v_r-v_l) 即为该方案的长度。
题意
有一个 (n imes m) 的迷宫,其中有一些地方不能走。最外层一定不能走。
有 (x) 个鬼在移动。每一秒,它们可以到达新的位置或不动,但必须遵循以下原则:
- 没有两个鬼在一个位置
- 没有两个鬼在某一秒交换了位置
给出鬼的位置和它们要去的终点,问至少需要多少秒才能使所有鬼到达自己要去的终点。保证有解
(4 leq n,m leq 16,1 leq x leq 3)
题解
可以给可用的格子标号,然后设计状态 ((a,b,c)),表示三个鬼分别在标号为 (a,b,c) 的格子的最小步数。
观察到可用的格子不超过 (16^2-16 imes 4+4leq 196),所以我们可以把状态 ((a,b,c)) 表示为一个二进制数,其高八位代表 (a),中间八位代表 (b),最低的八位代表 (c)。搜索即可。
观察到鬼的数量可能小于 (3)。这种情况下,将 (b/c) 表示为两个不同且大于最大标号的数即可,但不要超过 (256)。
输入使用 fgets
,否则会 TLE。
题意
给定一个 (1) 到 (n) 的排列 (p)。每次操作可以选择一个连续子序列,将其剪切到另外一个位置。求至少需要几次操作能将其变成 (1,2,...,n)。
(1 leq n leq 9)
题解
考虑 IDA*。定义 (p_i) 的后继为 (p_i+1)。观察到,一次操作至多改变 (3) 个数的后继。最好的情况下,至多使 (3) 个数的后继变为正确的。显然,我们不会将一个数已经正确的后继破坏掉。设计估价函数:(h() = dfrac { ext{dif}}{3})。其中 ( ext{dif}) 表示不正确的后继数量。
那么,我们可以估计该状态是否合法:( ext{nowstep}+h() leq ext{maxstep})。值得一提的是,(h()) 可能不是整数,这个时候需要将不等式等价变形为 ( ext{nowstep} imes 3+ ext{dif} leq ext{maxstep} imes 3)。
题意
给定容量为 (n) 的背包,有两种物品,均有无限个,体积和价值分别为 (S_1,V_1,S_2,V_2),求最大价值。
(1 leq n,S_i,V_i leq 10^9)
题解
首先,知道了某一种物品的个数,显然可以计算出另一种物品的个数。
设 (S_1<S_2)。
如果 (dfrac {n}{S_2} < sqrt{n}),那么可以在不高于 (O(sqrt{n})) 的时间内求出所有可能的第二种物品个数。
否则,从 (0) 到 (S_1) 枚举可能的第二种物品个数,从 (0) 到 (S_2) 枚举可能的第一种物品的个数。显然,这仍然不高于 (O(sqrt{n}))。
正确性证明:如果第一种物品的性价比比第二种高,那么如果第二种物品有 (S_1) 件,显然可以通过将其中 (S_1 imes S_2) 的体积替换为 (S_2) 件第一种物品来获得更高的性价比。反之亦然。
题意
最初,你的手上有一个正整数 (x)。你可以选择一个已经计算出 (x^a),如果当前手上的数为 (x^b),那么你可以选择计算出 (x^{a+b}) 或 计算出 (x^{b-a})(当 (b>a) 时),都需要花费一次操作。求至少需要几次操作才能计算出 (x^n)。
(1 leq n leq 1000)
题解
考虑倍增,次数显然不超过 (11)。
迭代加深搜索。对于限制为 (m) 步,当前准备使用第 (x) 步的状态,考虑剪枝:
-
当前的幂 (b) 经过 (m-x+1) 次平方后无法到达 (n),即 (b imes 2^{m-x+1}<n)
-
当前的幂 (b) 已经被计算过
即可。
题意
将 (dfrac{a}{b}) 分解为几个分子为 (1) 的不同分数之和。保证给出数据有解,且分母最大的分数分母 (leq 10^7)。
给出分数个数最小的解。如果仍有多解,给出最大分母最小的解;如果仍有多解,给出任意解。
题解
迭代加深搜索。
令枚举的分母 (x) 单调递增,如果剩余的分数大小为 (dfrac{a}{b}),对于限制为 (m) 步,当前准备使用第 (i) 步的状态,考虑剪枝:
- 如果 (i=m) 且 (a eq 1),那么当前状态无法得出解
- (xin [ max{lst+1,dfrac ba +1},(m-i+1) imes b div a )),其中 (lst) 表示上一次枚举到的分母
证明第二个剪枝。对于 (x) 的下界,显然应该使 (dfrac 1x<dfrac ab)。推一下可以得到 (dfrac ba < x),即 (x geq dfrac ba +1)。
对于 (x) 的上界:因为后面的分数都比 (dfrac 1x) 小,那么如果后面的分数全部选 (dfrac 1x) 仍然无法大于 (dfrac ab),即:
反过来讲,当 (x < b(m-i+1)div a) 时,才可能存在可行解。
注意通分时可能会溢出,需要使用 long long
。