图中最长的周期


18

给定有向图,输出最长的周期。

规则

  • 允许使用任何合理的输入格式(例如,边列表,连接矩阵)。
  • 标签并不重要,因此您可以对需要和/或期望的标签施加任何限制,只要它们不包含输入中未提供的其他信息即可(例如,您不能要求循环中的节点为标有整数,其他节点标有字母字符串)。
  • 循环是指所有连接的节点序列,除了作为循环开始和结束的节点([1, 2, 3, 1]是循环,但[1, 2, 3, 2, 1]不是循环节点)以外,没有节点重复。
  • 如果该图是非循环的,则最长周期的长度为0,因此应产生一个空输出(例如,空列表,完全没有输出)。
  • 在循环中的节点列表末尾重复第一个节点是可选的([1, 2, 3, 1][1, 2, 3]表示相同的循环)。
  • 如果存在多个相同长度的循环,则可以输出其中一个或全部。
  • 允许使用内置函数,但是如果您的解决方案使用一个内置函数,则建议您包括一个不使用琐碎内置函数的替代解决方案(例如,输出所有周期的内置函数)。但是,替代解决方案完全不会计入您的分数,因此它是完全可选的。

测试用例

在这些测试案例中,输入以边列表的形式给出(其中第一个元素是源节点,第二个元素是目的节点),输出是不重复第一个/最后一个节点的节点列表。

[(0, 0), (0, 1)] -> [0]
[(0, 1), (1, 2)] -> []
[(0, 1), (1, 0)] -> [0, 1]
[(0, 1), (1, 2), (1, 3), (2, 4), (4, 5), (5, 1)] -> [1, 2, 4, 5]
[(0, 1), (0, 2), (1, 3), (2, 4), (3, 0), (4, 6), (6, 8), (8, 0)] -> [0, 2, 4, 6, 8]
[(0, 0), (0, 8), (0, 2), (0, 3), (0, 9), (1, 0), (1, 1), (1, 6), (1, 7), (1, 8), (1, 9), (2, 1), (2, 3), (2, 4), (2, 5), (3, 8), (3, 1), (3, 6), (3, 7), (4, 1), (4, 3), (4, 4), (4, 5), (4, 6), (4, 8), (5, 0), (5, 8), (5, 4), (6, 0), (6, 1), (6, 2), (6, 3), (6, 4), (6, 5), (6, 6), (6, 7), (6, 9), (7, 0), (7, 1), (7, 2), (7, 3), (7, 4), (7, 5), (7, 8), (7, 9), (8, 0), (8, 1), (8, 2), (8, 5), (8, 9), (9, 1), (9, 2), (9, 3), (9, 4), (9, 5), (9, 6)] -> [0, 9, 6, 7, 8, 2, 5, 4, 3, 1]
[(0, 0), (0, 2), (0, 4), (0, 5), (0, 7), (0, 9), (0, 11), (1, 2), (1, 4), (1, 5), (1, 8), (1, 9), (1, 10), (2, 0), (2, 1), (2, 3), (2, 4), (2, 5), (2, 6), (3, 0), (3, 1), (3, 5), (3, 6), (3, 7), (3, 8), (3, 9), (3, 11), (4, 1), (4, 3), (4, 7), (4, 8), (4, 9), (4, 10), (4, 11), (5, 0), (5, 4), (5, 6), (5, 7), (5, 8), (5, 11), (6, 0), (6, 8), (6, 10), (6, 3), (6, 9), (7, 8), (7, 9), (7, 2), (7, 4), (7, 5), (8, 8), (8, 9), (8, 2), (8, 4), (8, 7), (9, 0), (9, 1), (9, 2), (9, 3), (9, 6), (9, 10), (9, 11), (10, 8), (10, 3), (10, 5), (10, 6), (11, 2), (11, 4), (11, 5), (11, 9), (11, 10), (11, 11)] -> [0, 11, 10, 6, 9, 3, 8, 7, 5, 4, 1, 2]

在所有示例中,输出都是从索引最小的节点开始的。这是一个要求吗?
Dada

@Dada不,那只是测试用例的巧合。输出应从循环中的第一个节点开始(并且可以选择结束)。
Mego

您应该选择一种格式,带端点或不带端点是任意的,并且对挑战没有任何影响。
Magic Octopus Urn

5
@carusocomputing我不同意。如果保留,则最后一个节点是隐式的(因为它与第一个节点相同)。允许选择是否重复第一节点可以使打高尔夫球有更大的自由度。
Mego

Answers:


4

Mathematica,80 58字节

感谢郑焕敏节省了多达22个字节

(FindCycle[#,∞,All]/.{}->{Cases[#,v_v_]})[[-1,;;,1]]&

U+F3D5代表的三字节私人使用字符\[DirectedEdge]。具有第一个参数的纯函数#应该是有向边的列表。发现All在大多数长度的周期InfinityGraph@#,并将其替换为自循环的列表空列表。循环用边列表表示,并按长度排序,因此我们采用最后一个这样的循环,然后从其所有边开始采用第一个参数,以便获得指定输出格式的顶点列表。

如果仅Mathematica将循环视为一个长度循环1(认真地AcyclicGraphQ @ CycleGraph[1, DirectedEdges -> True]给出True),那么我们可以保存另一个26字节:

FindCycle[#,∞,All][[-1,;;,1]]&

1
您不需要,MaximalBy因为的结果FindCycle已经按长度排序(最后一个元素是最长的)。另外,的第一个参数FindCycle可以是\[DirectedEdge](而不是Graph)的列表。另外,你可以使用2个字节;;(= 1;;-1,而不是3个字节),AllPart保存字节。-22个字节(58个字节):(FindCycle[#,∞,All]/.{}->{Cases[#,v_v_]})[[-1,;;,1]]&
JungHwan Min'1

3

Haskell中157个154 150字节

import Data.List
g#l=nub[last$(e:d):[d|p==last q||e`elem`init d]|d@(p:q)<-l,[e,f]<-g,p==f]
h g=snd$maximum$((,)=<<length)<$>[]:until((==)=<<(g#))(g#)g

在线尝试!

感谢@Laikoni和@Zgrab节省了一堆字节!

这是一个效率很低的程序:

第一个函数#采用路径l列表(数字列表),并尝试通过将的l每个可能边(长度2的列表)添加g到的每个元素来扩展元素l。出现这种情况仅当该元素l是不是已经一个周期如果将被添加新节点尚未包含在的元素l。如果已经是一个循环,则不添加任何内容,而是将其再次添加到新的路径列表中;如果可以扩展,则将扩展的路径添加到新列表中,否则,不将其添加到新列表中。

现在,该函数h反复尝试扩展这些路径(从边缘列表本身开始),直到到达固定点为止,即,我们无法再扩展任何路径。目前,清单中只有周期。然后,只需选择最长的周期即可。显然,循环在此列表中出现了多次,因为一个循环的每个可能的循环旋转都是一个循环。


您可以将括号放在中(p:q)<-l
Laikoni '17

并且使用<$>代替map应该在中保存另一个字节((,)=<<length)<$>[]:
Laikoni '17

@Laikoni非常感谢!
瑕疵

在最后一行之后,您还有多余的空间。另外,这样做d@(p:q)<-l可以节省一些字节。
Zgarb

哦,d@(p:q)真的很好,谢谢您给我看!
瑕疵

2

Pyth,20个字节

eMefqhMT.>{eMT1s.pMy

测试套件

像示例中一样获取边缘列表。

说明:

eMefqhMT.>{eMT1s.pMy
eMefqhMT.>{eMT1s.pMyQ    Variable introduction
                   yQ    Take all subsets of the input, ordered by length
                .pM      Reorder the subsets in all possible ways
               s         Flatten
                         (This should be a built in, I'm going to make it one.)
   f                     Filter on (This tests that we've found a cycle)
    qhMT                 The list of first elements of edges equals
           eMT           The last elements
         .>   1          Rotated right by 1
        {                Deduplicated (ensures no repeats, which would not be a
                         simple cycle)
  e                      Take the last element, which will be the longest one.
eM                       Take the last element of each edge, output.

2

Bash + bsdutils,129个字节

sed 's/^\(.*\) \1$/x \1 \1 x/'|sort|(tsort -l>&-)|&tr c\\n '
 '|sed 's/x //g'|awk 'm<NF{m=NF;gsub(/[^0-9 ] ?/,"");print}'|tail -1

tsort完成了所有繁重的工作,但其输出格式相当独特,并且无法检测到长度为1的循环。请注意,这不适用于GNU tsort。

验证

--- t1 ---
0
--- t2 ---
--- t3 ---
0 1
--- t4 ---
1 2 4 5
--- t5 ---
0 2 4 6 8
--- t6 ---
0 2 1 6 3 7 4 8 9 5
--- t7 ---
0 11 10 3 1 2 4 7 5 8 9 6

2

的JavaScript(ES6),173个 163 156 145 139字节

@Neil节省了5个字节

f=(a,m,b=[])=>a.map(z=>!([x,y]=z,m&&x-m.slice(-1))&&b.length in(c=(n=m||[x],q=n.indexOf(y))?~q?b:f(a.filter(q=>q!=z),[...n,y]):n)?b=c:0)&&b

测试片段


确定切换到普通的旧版本map可以为您节省几个字节吗?
尼尔

@Neil必须是.filter().map(),所以几乎可以肯定不是。切换为我节省了10个字节(尽管它不像现在那样充分打高尔夫球)
ETHproductions'Jan

我看不到您使用理解结果,因此a.filter(z=>!e).map(z=>d)可以使用代替使用a.map(z=>e?0:d)
尼尔

没错,我可以合并所有内容以节省5个字节。我只是意识到我也不需要a+a?:-)
ETHproductions'Jan

请下票的人解释一下出什么问题了吗?它会产生不正确的输出吗?
ETHproductions

2

Haskell109108字节

import Data.List
f g=last$[]:[b|n<-[1..length g],e:c<-mapM(\_->g)[1..n],b<-[snd<$>e:c],b==nub(fst<$>c++[e])]

蛮力解决方案:生成所有长度递增的边列表,直到输入的长度,保留那些为循环的,返回最后一个。以格式提取图形[(1,2),(2,3),(2,4),(4,1)]在线尝试!

说明

f g=                    -- Define function f on input g as
  last$                 -- the last element of the following list
  []:                   -- (or [], if the list is empty):
  [b|                   --  lists of vertices b where
   n<-[1..length g],    --  n is between 1 and length of input,
   e:c<-                --  list of edges with head e and tail c is drawn from
    mapM(\_->g)[1..n],  --  all possible ways of choosing n edges from g,
   b<-[snd<$>e:c],      --  b is the list of second elements in e:c,
   b==                  --  and b equals
    nub(fst<$>c++[e])]  --  the de-duplicated list of first elements
                        --  in the cyclic shift of e:c.

我花了一段时间才终于了解发生了什么,检查路径/循环的部分真的很聪明,我很惊讶!
瑕疵

@flawr谢谢!好吧,isacag似乎在我之前使用了基本相同的算法。
Zgarb

0

MATLAB,291260字节

采用一个邻接矩阵A,其中的边(i,j)1in中的a表示A(i,j)A在所有其他条目中为零。输出是最长周期的列表。如果根本没有循环,则该列表为空;如果有循环,则该列表包括开始和终点。它使用1基于索引的索引。

该解决方案不使用任何与图形相关的内置函数。

function c=f(A);N=size(A,1);E=eye(N);c=[];for j=1:N;l=g(j);if numel(l)>numel(c);c=l;end;end;function p=g(p)if ~any(find(p(2:end)==p(1)))e=E(p(end),:)Q=find(e*A)k=[];for q=Q;if ~ismember(q,p(2:end))n=g([p,q]);if numel(n)>numel(k);k=n;end;end;end;p=k;end;end;end

不幸的是,它不能在TryItOnline中运行,因为它在函数中使用函数,这是递归的。稍加修改,即可在octave-online.net尝试

对于最后一个测试用例,我发现了另一个最长的周期[0 2 1 4 3 5 7 8 9 11 10 6 0](此表示法使用基于0的索引)

说明

此处的基本方法是,我们从每个节点执行BFS,并注意除开始节点外,我们不要再次访问任何中间节点。有了这个想法,我们可以收集所有可能的周期,并轻松地选择最长的周期。

function c=f(A);
N=size(A,1);
E=eye(N);
c=[]; % current longest cycle
for j=1:N;                                      % iterate over all nodes
    l=getLongestCycle(j);                       % search the longest cycle through the current node
    if numel(l)>numel(c);                       % if we find a longer cycle, update our current longest cycle
        c=l;
    end;

end;

    function p=getLongestCycle(p);              % get longest cycle from p(1) using recursion
        if ~any(find(p(2:end)==p(1)));          % if we just found a cycle, return the cycle do nothing else, OTHERWISE:
            e=E(p(end),:);                      % from the last node, compute all outgoing edges
            Q=find(e*A);                        
            k=[];                               
            for q=Q;                            % iterate over all outogoin edges
                if ~ismember(q,p(2:end));       % if we haven't already visited this edge,
                    n=getLongestCycle([p,q]);   % recursively search from the end node of this edge
                    if numel(n)>numel(k);       % if this results in a longer cycle, update our current longest cycle
                        k=n;
                    end;
                end;
            end;
            p=k;
        end;
    end; 
end
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.