如何确定完全断开连接环的最小数量?

时间:2016-05-09 09:12:59

标签: python algorithm graph

这是关于checkio - Break Rings的问题,但我只能通过测试所有可能的中断方式并找到最小的中断方式来使用O(n * 2 ^ n)复杂度的错误方法。

问题: 铁匠给了他的学徒一项任务,命令他们选择戒指。学徒还不熟练,因此,一些(诚实地说,大多数)戒指出现了联系在一起。现在他要求你帮助分离戒指,并决定如何打破足够的戒指,以便获得最大数量的戒指。

所有戒指都已编号,您将被告知哪些戒指已连接。该信息以一系列集合的形式给出。每组描述连接的环。例如:{1,2}表示第1和第2环连接。您应该计算我们需要断开多少环以获得最大的单独环。每个环的编号范围为1到N,其中N是环的总数。

https://static.checkio.org/media/task/media/0d98b24304034e2e9017ba00fc51f6e3/example-rings.svg

例如环

(抱歉,我不知道如何将mac中的svg更改为照片。)

在上图中,您可以看到连接:({1,2},{2,3},{3,4},{4,5},{4,6},{6,5}) 。这里的最佳解决方案是打破3个环,制作3个完整和单独的环。结果是3。

输入:关于连接环的信息,作为具有整数的集合的元组。

输出:要以整数形式断开的响铃次数。

只有在测试用例很小的情况下才有效,所以它不实用(我猜它甚至无法通过测试)

from functools import reduce
import copy

def break_rings(rings):
    max_ring = max(reduce(set.union,rings))
    rings = list(rings)
    possible_set = [list(bin(i)[2:].rjust(max_ring,'0')) for i in range(2**max_ring)]
    possible_set = [list(map(int,j)) for j in possible_set]
    min_result = max_ring
    for test_case in possible_set:
        tmp = copy.copy(rings)
        tmp2 = copy.copy(rings)
        for index, value in enumerate(test_case):
            if value:
                for set_connect in tmp:
                    if index+1 in set_connect and set_connect in tmp2:
                        tmp2.remove(set_connect)
        if not tmp2:
            min_result = min(sum(test_case),min_result)
    return min_result

所以,我认为它必须考虑关于图表的算法,但我不知道我面临的是什么样的问题。

你能帮我改进算法吗?

感谢您查看此问题!

1 个答案:

答案 0 :(得分:3)

您可以将此视为一种名为vertex cover的图形问题。

绘制每个环带有顶点的图形,以及每个连接的边缘,即每对连接环。

您的任务是以最小的破损断开环。如果任一边缘的环断裂,则连接断开。换句话说,您需要选择一组环(顶点),以便每个连接(边缘)都与所选环相关。

这正是顶点覆盖问题。

不幸的是,顶点覆盖是NP完全的,所以目前还没有任何非指数算法。

我建议先提前拒绝不良案例,以提高算法的速度。例如,使用回溯算法来确定每个环是否打破它。如果你选择不打破它,你可以立即得出结论,许多其他戒指必须被打破。