グラフの最長サイクル


18

有向グラフを指定すると、最長のサイクルが出力されます。

ルール

  • 任意の妥当な入力形式が許可されます(エッジのリスト、接続性マトリックスなど)。
  • ラベルは重要ではないので、入力に与えられていない追加情報が含まれていない限り、あなたが必要または希望するラベルに制限を課すことができます(たとえば、サイクル内のノードが整数でラベル付けされ、他のノードはアルファベット文字列でラベル付けされます)。
  • サイクルは、すべて接続されているノードのシーケンスであり、サイクルの開始および終了であるノード以外のノードは繰り返されません([1, 2, 3, 1]サイクルですが、そうで[1, 2, 3, 2, 1]はありません)。
  • グラフが非循環の場合、最長サイクルの長さは0であるため、空の出力(空のリスト、出力なしなど)が生成されます。
  • サイクル内のノードのリストの最後で最初のノードを繰り返すことはオプションです(同じサイクル[1, 2, 3, 1][1, 2, 3]示します)。
  • 同じ長さのサイクルが複数ある場合、それらのいずれかまたはすべてが出力される場合があります。
  • ビルトインは使用できますが、ソリューションで使用する場合は、簡単なビルトインを使用しない代替ソリューション(すべてのサイクルを出力するビルトインなど)を含めることをお勧めします。ただし、代替ソリューションはスコアにはまったくカウントされないため、完全にオプションです。

テストケース

これらのテストケースでは、入力はエッジのリスト(最初の要素はソースノードで、2番目の要素は宛先ノード)として与えられ、出力は最初/最後のノードの繰り返しのないノードのリストです。

[(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いいえ、それはテストケースとの偶然の一致です。出力は、サイクルの最初のノードから開始(およびオプションで終了)する必要があります。
メゴ

エンドポイントありまたはなしの形式を選択する必要があり、チャレンジには何も追加しません。
魔法のタコUr

5
@carusocomputing同意しない。省略した場合、最後のノードは暗黙的です(最初のノードと同じであるため)。最初のノードを繰り返すかどうかの選択を許可すると、ゴルフの自由度が高まります。
メゴ

回答:


4

Mathematica、80 58バイト

ジョンファンミンのおかげでなんと22バイト節約

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

は、をU+F3D5表す3バイトの私用文字\[DirectedEdge]です。#有向辺のリストであると期待される最初の引数を持つ純粋な関数。見つけたのAll最大で長さのサイクルInfinityではGraph@#、その後、自己ループのリストを空のリストを置き換えます。サイクルはエッジのリストとして表され、長さでソートされているため、最後のそのようなサイクルを取得し、すべてのエッジから最初の引数を取得して、指定された出力形式の頂点のリストを取得します。

Mathematicaは長さのサイクルとしてループを扱う場合にのみ、1AcyclicGraphQ @ 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]]&
ジョンファンミン

3

Haskell157154150バイト

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
ライコニ

<$>代わりにを使用すると、にmap別のバイトが保存され((,)=<<length)<$>[]:ます。
ライコニ

@ライコニありがとうございます!
-flawr

最終行の後に余分なスペースがあります。また、これd@(p:q)<-lによりいくつかのバイトが節約されます。
ズガルブ

ああ、d@(p:q)本当にいいです、見せてくれてありがとう!
flawr

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

理解の結果を使用しているのを見ないので、代わりにを使用a.filter(z=>!e).map(z=>d)できますa.map(z=>e?0:d)
ニール

そうです、すべてを組み合わせて5バイト節約できます。そして、私はa+a?どちらも必要ないことに気づきました:
ETHproductions

ダウンボッターは何が悪いのか説明してくれますか?不正な出力が生成されますか?
-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

@flawrありがとう!さて、isaacgは基本的に同じアルゴリズムを使用していたようです。
ズガルブ

0

MATLAB、291 260バイト

Aエッジ(i,j)がaで示される隣接行列を取ります1にし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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.