我想了解解决Google Code Jam, Tutorial, Problem C的算法。到目前为止,我写了my own basic implementation来解决这个小问题。我发现它无法处理大问题(复杂度O(min(n,2 * k)!在较大的数据集中是30!)。
我发现了这个solution page,但解决方案当然没有记录(上下文有时间限制)。我看到至少有一个解决方案使用Union Find data structure,但我不明白它是如何应用的。
有没有人知道有一个解决这些问题的算法的页面,而不仅仅是代码?
答案 0 :(得分:1)
不确定是否有更好的方法来处理GCJ - Hamiltonian Cycles的近似重复,但这是我的回答:
基于O(2 k )的解决方案使用inclusion-exclusion principle。鉴于 k 禁止边缘,有 2 k 这些子集边缘,包括集合本身和空集。例如,如果有3个禁带:{A,B,C},则会有2个 3 = 8个子集:{},{A},{B},{C},{ A,B},{A,C},{B,C},{A,B,C}。
对于每个子集,您计算至少包含该子集中所有边的周期数。如果包含边 s 的周期数为 f(s) 且 S < / em> 是所有禁用边的集合,然后通过包含 - 排除原则,没有任何禁止边的循环数是:
sum, for each subset s of S: f(s) * (-1)^|s|
其中| s |是 s 中元素的数量。换句话说,任意边的周期数总和减去至少有1个禁止边的周期数加上至少有2个禁边的数字,。 ..
计算 f(s) 并非易事 - 至少我找不到一种简单的方法。你可以在阅读之前停下来思考它。
要计算 f(s) ,请从未与任何 s 相关的节点的排列数开始节点。如果 m 此类节点,则 m !如你所知,排列。调用排列数 c 。
现在检查链中 s 的边缘。如果存在任何不可能的组合,例如涉及3条边的节点或 s 中的子循环,则 f(s) 是0。
否则,对于每个链增量 m 增加1并将 c 乘以 2米 即可。 (有 m 的位置将链放在现有的排列中,因子2是因为链可以向前或向后。)最后, f(s) 是 c /( 2m )。最后一个分区将排列转换为循环。
答案 1 :(得分:0)
对输入数据施加的重要限制是禁止边数k <= 15。
您可以通过包含和排除来继续:
由于F只有2 ^ k <= 32768个子集(所有禁止边的集合),因此你将得到一个合理的运行时限。
Google代码问题analysis的Endless Knight使用了类似的想法。
答案 2 :(得分:0)
汉密尔顿循环问题是旅行商问题的特例(通过将两个城市之间的距离设置为有限常数(如果它们相邻且无穷大)获得。)
这些是NP完全问题,简单来说就是没有快速解决问题。
要解决Google Code Jam上的问题,您应该了解Algorimths Analysis and Design,虽然它们可以在指数时间内解决,但不要担心Google非常清楚。 ;)
以下来源应足以让您入门:
麻省理工学院讲座视频:“算法简介”
书籍:算法导论[Cormen,Leiserson,Rivest&amp;斯坦]
算法设计手册[Steven S. Skiena]