解决Google Code Jam教程问题C的算法

时间:2011-01-14 17:01:39

标签: algorithm graph

我想了解解决Google Code Jam, Tutorial, Problem C的算法。到目前为止,我写了my own basic implementation来解决这个小问题。我发现它无法处理大问题(复杂度O(min(n,2 * k)!在较大的数据集中是30!)。

我发现了这个solution page,但解决方案当然没有记录(上下文有时间限制)。我看到至少有一个解决方案使用Union Find data structure,但我不明白它是如何应用的。

有没有人知道有一个解决这些问题的算法的页面,而不仅仅是代码?

3 个答案:

答案 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。

您可以通过包含和排除来继续:

  • 计算所有周期的数量((n-1)!),
  • 对于每个禁用边e,减去包含它的周期数((n-2)!/ 2,除非n非常小),
  • 对于每对禁止边e,f,添加包含它们的周期数(这将取决于是否触摸e和f),
  • 对于每个三元组...,减去......,
  • 等。

由于F只有2 ^ k <= 32768个子集(所有禁止边的集合),因此你将得到一个合理的运行时限。

Google代码问题analysisEndless Knight使用了类似的想法。

答案 2 :(得分:0)

汉密尔顿循环问题旅行商问题的特例(通过将两个城市之间的距离设置为有限常数(如果它们相邻且无穷大)获得。)

这些是NP完全问题,简单来说就是没有快速解决问题

要解决Google Code Jam上的问题,您应该了解Algorimths Analysis and Design,虽然它们可以在指数时间内解决,但不要担心Google非常清楚。 ;)

以下来源应足以让您入门:

  1. 麻省理工学院讲座视频:“算法简介”

  2. TopCoder Tutorials

  3. http://dustycodes.wordpress.com

  4. 书籍:算法导论[Cormen,Leiserson,Rivest&amp;斯坦]

  5. 算法设计手册[Steven S. Skiena]