分类目录归档:acm

面试题一则

有节点数为n的一棵树,每个节点i有一个权值w[i],节点i到节点j的距离d[i][j]定义为在树上节点i和节点j之间有多少条边。定义f(i) = sum{w[j] * d[i][j] | -1 < j < n},求min{f(i) | -1 < i < n}。 题源来自qoshi

基本思路还是树型DP,希望能根据相邻节点的f值计算出当前节点的f值。具体的思路是,如果指定了深搜的起点为0,那么每个节点都会将树上的其他节点分为两个部分,一部分是离0点近的(设为A类节点),一部分是离0点远的(设为B类节点)。

具体的状态转移,包括4个矩阵:lw[i],记录的是某个节点划分出的属于A类节点的权值和;rw[i]记录属于B类节点的权值和;lws[i]记录的是sum{ w[j] * d[i][j] | j属于i划分出的A类节点};rws[i]记录的是sum{ w[j] * d[i][j] | j属于i划分出的B类节点}。由此f(i) = lws[i] + rws[i]。使用这4个矩阵是因为,沿着某条边走一步后,某个节点的这4个值可以根据相邻节点的信息O(1)地得到。

其中rws[i],需要从自底向上的构造,也就是用了深搜遍历完所有孩子节点后,再计算父节点的值;而lws[i],则是自顶向下的计算。

具体的程序如下:

只有自己做的一些测试数据,没有找到OJ版本的题目:

输入1:

输出1:

输入2:

输出2:

[HDU 4836]

The Query on the Tree

2014年百度之星复赛的第二题,看了官方题解才会做。

个人觉得这题有意思的地方有:
(1)通过记录DFS顺序+树状数组的方式,动态更新每一棵子树的权值和;
(2)树根改变的时候,根据父子关系巧妙地不修改数据求出当前子树权值和。

[POJ 3074]

Sudoku

题意:求解数独问题。

思路:用DLX解。首先,需要将格盘建模为DLX用到的十字链表。数字之间的互斥关系共有4种,因此可以对”每行每个数字”、”每列每个数字”、”每九宫格每个数字”、”每个格点位置”分别建立一个链表列,也即总共是9*9+9*9+9*9+9*9=324个链表列,十字链表中每一行表示在某格点放置某数,分别与4个对应的链表列相交。

然后,就是正式的深搜了。在深搜的每一层:
1) 在十字链表表头中找出元素最少且非零的一列,将该列以及列中包含的所有行元素从十字链表中移除;
2) 枚举列中的每行(作为解的一部分),将与该行相交的所有其他列以及这些列包含的行元素在十字链表中移除,递归下一层深搜;
3) 递归返回时,需要将2)中移除的相应列和行添加回十字链表;
4) 所有行枚举完毕时,需要将1)中移除的相应列和行添加回十字链表。

注意,对链表进行增删操作时,需要以相反的顺序修改各结点。同时,也需要动态维护表头的元素数。

没有想出太好的优化。c++下跑了188ms。

ps:
记得最早是11年寒假时,和Arios一起留校做MCM时听他提到,当时那道建模题可以用Dancing Links解。一晃眼,3年多过去了,我这才开始读Knuth的这篇论文

文章本身的算法不算难懂,因为深搜的优化是很多问题都要用到的。之前做题的时候,确实也有考虑过对链表进行动态调整,但发现无法根本上降低时间复杂度(依然是指数时间)后,也没有深究了。而Knuth这篇论文里介绍的DLX,通过对十字链表进行动态调整从而尽量保持搜索树最优,可以说是在通常情况下将深搜优化到了极致。

做这题数独时,在链表建模那一步想了大概一整天时间。主要是数独问题元素间的互斥情况比较复杂。一开始只用了前3种互斥关系,发现表头记录的元素数无法正确维护,每次暴力重算并加了些优化后竟然勉强过了。然后就一直在想正解。想出应该增加第4种互斥关系后,又用了很长时间才推出要移除与当前行元素互斥的所有行。最后对链表的修改要比一开始想的要复杂得多。

查了下status,Arios在5年前作出的时间是32ms,并且是1A 47ms过的。实在是很强。

[SPOJ 2832]

Find The Determinant III

数论

题意:输入一个最多200*200的行列式a和一个正整数p,求|a|%p。

分析:集训队论文集里《欧几里得算法的应用》的第一道例题。非常巧妙,用GCD思想作模系统下的高斯消元。实现的时候大致要注意以下几点:
(1)尽量减少辗转相除的次数;
(2)尽量少用模运算;
(3)消元过程中算出答案为0提前结束;
(4)尽量避免整行swap;
(5)手动解释输入;
(6)尽量不用long long。

TLE了一整晚后,成功刷入第一版

[POJ 1379]

Run Away

模拟退火

题意:在最大10000*10000平面的上有最多1000个洞,求平面上的某个点,使得其离所有洞的最近距离最大化。输出精度为小数点后1位。

分析:按照模拟退火的思想,在平面上随机取10个点,将单次移动的步长设为算法中的温度,移动方向随机。初始温度设为矩形对角线长的一半,每轮迭代后温度降为之前的0.8。每轮迭代时,对于每个点,按照当前步长尝试移动50次,如果目标点更优则更新位置,否则维持原位。当温度降低到0.001以下时,终止算法,并取10个点中的最优值输出。

ps:非常山寨的实现方式,没有考虑目标位置并非更优时仍进行移动的方案。本题的数据也是很松,只要控制好初始点数,温度下降幅度以及迭代次数,时限范围内很易求得最优位置。

[POJ 1263]

Reflections

计算几何

题意:给出二维平面上一个起始点和光线方向,以及平面上最多25个反射球面,问光线每次反射时击中的球编号。当不再击中球面或是反射了超过10次后终止计算。题目保证起始点不在球内,且不会沿切线方向击中球。

分析:由于数据量不大,可以每次计算出光线方向上与每个球的交点,按距离排序后取最近的一个点,根据入射角和切点算出反射角。迭代以上过程10次即可。

ps:分类表最后一题了。还差得远,继续努力。

[POJ 3336]

ACM Underground

计算几何

题意:给出一个起点和终点坐标,以及最多100条线段和3000个黑点,问从起点出发是否能到达终点。移动过程中存在以下限制:只能沿线段移动,如果遇到交点,可以转移到另一条线段上。如果移动过程中遇到黑点:若黑点在交点处,则无法转移到其他线段;否则无法跨越黑点继续沿线段移动。

分析:数据范围很小,可以首先枚举出所有的点,并对每条线段计算出所有在其上的点,排序,对所有相邻可达点连边,最后从起点出发深搜即可。这里的关键在于,处理某条线上的点时,若在相同位置遇到了黑点和白点,那么需要在后续处理中忽略所有该位置上的点。每次仅当两个相邻点都为白点时才连边,这样可以确保仅能通过有效交点转移到其他线段,且不会越过在单一线段上的黑点。

[POJ 2975] & [POJ 2960]

Nim

博弈

题意:Nim问题的必胜策略为,取当前各堆石子数的异或和,若为0则为必败局面;否则取走部分石子使得异或和变为0,便可使对方陷入必败局面。问题为,给出一个局面,问有多少种不同的必胜策略。

分析:很简单,既然必胜策略已知,那么可以算出需要改变那些位才能转化为必败局面。由于,每次只允许改变一堆石子的数目,目标石子数小于等于当前石子数时对应一种必胜策略。最后算一算有多少个堆满足该条件即可。

稍微增加难度的话,就是下面这题:

S-Nim

博弈

题意:对于基本的Nim问题,增加如下限制:每次取走的石子数必须在集合S中,若剩余石子不足则可以全部取走。问题是,对于某局面,是否存在必胜策略。

分析:对于给定的S集,考虑将剩余石子的数目划分类别。具体来说,将剩余为0或无法一步到达第0类的石子数设为第0类,可以一步到达第0类(但无法到达第1类)的设为第1类,并依次类推:可以一步到达第0…i-1类且无法到达第i类的设为第i类。这样分类之后,便可以将第i类堆与原Nim问题中的堆石子数为i对应起来。其依据在于,第i类总是能一步到达更低的类别,但无法保持当前状态(或者到达更高类别时会在下一步(由对手操作)回到该类)。由此,便可以对当前各堆的类别数取异或和,并按照结果是否为0来判定是否为必败局面。

[POJ 1478]

Island of Logic

枚举

题意:有一个岛上有3种(仅从外观无法分辨的)生物:神(只说真话)、人(白天说真话,晚上说谎话)、鬼(只说谎话)。给你一段长为n的聊天记录,要求尽量推断出更多的事实。聊天记录中最多涉及5个生物。

分析:事实分为两种,(1)生物的种族、(2)当前时间。基本思路是,可以枚举出所有的事实组合,并根据聊天记录来判断可行性。如果可行,则记录该事实组合。记录时,对于各个生物以及时间分别使用一个位相量,以逻辑或的形式记录其所有可能的取值。最后,仅存在唯一可能取值的位相量即为“事实”。

ps:350题了。每做一题在poj上排名都会略微提升,有种在尸体堆里穿行的感觉。(前辈们失敬了)