Part One
一个巨大的连通图,断掉其中三个edge,从而分裂为两个独立的图。
jqt: rhn xhk nvd
rsh: frs pzl lsr
xhk: hfx
一开始确实没有头绪,搜索了相关的资料,发现此类问题是一个k-edge-connected graph的问题,一般会使用Karger’s algorithm来计算这个图的min_cut,这个图的min_cnt应该是3。
上图描述了算法的基本步骤
- 从图中随机选择一个edge
- 将这条边去除,边的两个顶点合并
- 重复这个过程,直到图中只剩下2个顶点,顶点之间的边就是min_cnt
- 由于是随机选择edge,有概率最终剩下的边多于min_cut
首先构建两张表,一个是顶点之间的mapping关系表,一个是顶点表,存储合并的其他顶点
DROP TABLE mapping;
CREATE TABLE mapping AS
WITH origin AS (
SELECT split_part(line, ': ', 1) as l, unnest(regexp_split_to_array(split_part(line, ': ', 2), ' ')) as r
FROM lance_input
), mapping AS (
SELECT l, r
FROM origin
UNION ALL
SELECT r, l
FROM origin
)
SELECT * FROM mapping;
DROP TABLE vertex;
CREATE TABLE vertex AS
SELECT distinct l as node, ARRAY[]::varchar[] as merged_nodes
FROM mapping;
然后实现karger算法,直到只剩下两个顶点
CREATE OR REPLACE FUNCTION kargers()
RETURNS VOID AS $$
DECLARE
random_left VARCHAR;
random_right VARCHAR;
vertex_cnt INTEGER;
BEGIN
WHILE TRUE LOOP
-- remain nodes
SELECT COUNT(*) FROM vertex INTO vertex_cnt;
EXIT WHEN vertex_cnt = 2;
-- find a random edge from mapping
SELECT l, r FROM mapping order by random() limit 1 INTO random_left, random_right;
-- merge nodes
UPDATE vertex SET merged_nodes = vertex.merged_nodes || t.merged_nodes || t.node
FROM (SELECT node, merged_nodes FROM vertex WHERE node = random_right) t
WHERE vertex.node = random_left;
-- remove vertex
DELETE FROM vertex WHERE node = random_right;
-- remove edges between vertexes of random edge
DELETE FROM mapping WHERE l IN (random_left, random_right) AND r IN (random_left, random_right);
-- update edges
UPDATE mapping SET r = random_left WHERE r = random_right;
UPDATE mapping SET l = random_left WHERE l = random_right;
END LOOP;
END;
$$ LANGUAGE plpgsql;
当算法成功的时候,mapping表应该是剩下三条边,且从顶点表中也可看出分成的两个图的顶点数量
postgres=# select * From mapping;
l | r
-----+-----
rhn | rzs
rhn | rzs
rhn | rzs
rzs | rhn
rzs | rhn
rzs | rhn
(6 rows)
postgres=# select * from vertex;
node | merged_nodes
------+-----------------------------------
rhn | {hfx,jqt,ntq,xhk,bvb}
rzs | {frs,rsh,lhk,cmg,lsr,nvd,pzl,qnr}
(2 rows)
当算法未成功的时候,mapping表中多于三条边,且顶点表中的记录也不正确。
postgres=# select * from vertex;
node | merged_nodes
------+-------------------------------------------------------
jqt | {}
rhn | {nvd,pzl,ntq,bvb,lsr,lhk,rsh,hfx,xhk,rzs,cmg,qnr,frs}
(2 rows)
postgres=# select * from mapping;
l | r
-----+-----
jqt | rhn
rhn | jqt
jqt | rhn
jqt | rhn
jqt | rhn
rhn | jqt
rhn | jqt
rhn | jqt
(8 rows)
Part Two
终于完成了所有25关