Chu kỳ dài nhất trong đồ thị


18

Cho một đồ thị có hướng, xuất ra chu kỳ dài nhất.

Quy tắc

  • Bất kỳ định dạng đầu vào hợp lý đều được cho phép (ví dụ: danh sách các cạnh, ma trận kết nối).
  • Các nhãn không quan trọng, vì vậy bạn có thể áp đặt bất kỳ hạn chế nào đối với các nhãn mà bạn cần và / hoặc mong muốn, miễn là chúng không chứa thông tin bổ sung không được cung cấp trong đầu vào (ví dụ: bạn không thể yêu cầu các nút theo chu kỳ là được gắn nhãn với số nguyên và các nút khác được gắn nhãn bằng các chuỗi chữ cái).
  • Một chu kỳ là một chuỗi các nút được kết nối và không có nút nào được lặp lại, ngoài nút đó là điểm bắt đầu và kết thúc của chu kỳ ( [1, 2, 3, 1]là một chu kỳ, nhưng [1, 2, 3, 2, 1]không phải là).
  • Nếu đồ thị là chu kỳ, chu kỳ dài nhất có độ dài 0 và do đó sẽ mang lại một đầu ra trống (ví dụ: danh sách trống, không có đầu ra nào cả).
  • Lặp lại nút đầu tiên ở cuối danh sách các nút trong chu trình là tùy chọn ( [1, 2, 3, 1][1, 2, 3]biểu thị cùng một chu kỳ).
  • Nếu có nhiều chu kỳ có cùng độ dài, bất kỳ một hoặc tất cả chúng có thể là đầu ra.
  • Nội dung được cho phép, nhưng nếu giải pháp của bạn sử dụng một, bạn được khuyến khích đưa vào một giải pháp thay thế không sử dụng các nội dung tầm thường hóa (ví dụ: một nội dung tạo ra tất cả các chu kỳ). Tuy nhiên, giải pháp thay thế sẽ không được tính vào điểm số của bạn, vì vậy nó hoàn toàn là tùy chọn.

Các trường hợp thử nghiệm

Trong các trường hợp thử nghiệm này, đầu vào được đưa ra dưới dạng danh sách các cạnh (trong đó phần tử đầu tiên là nút nguồn và phần tử thứ hai là nút đích) và đầu ra là danh sách các nút mà không lặp lại nút đầu tiên / cuối cùng.

[(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]

Trong tất cả các ví dụ của bạn, đầu ra của bạn bắt đầu với nút có chỉ số nhỏ nhất. Đây có phải là một yêu cầu?
Dada

@Dada Không, đó chỉ là sự trùng hợp với các trường hợp thử nghiệm. Đầu ra sẽ bắt đầu (và tùy chọn kết thúc) với nút đầu tiên trong chu trình.
Mego

Bạn nên chọn một định dạng, có điểm cuối hoặc không có tùy ý và không thêm gì vào thử thách.
Bạch tuộc ma thuật Urn

5
@carusocomputing Tôi không đồng ý. Nút cuối cùng được ẩn nếu bỏ đi (vì nó giống như nút đầu tiên). Cho phép lựa chọn có lặp lại nút đầu tiên hay không cho phép chơi gôn nhiều hơn.
Mego

Câu trả lời:


4

Toán học, 80 58 byte

Đã tiết kiệm được 22 byte nhờ JungHwan Min

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

là ba ký tự sử dụng riêng tư U+F3D5đại diện \[DirectedEdge]. Hàm thuần túy với đối số đầu tiên #dự kiến ​​là một danh sách các cạnh được định hướng. Tìm các Allchu kỳ có độ dài tối đa Infinitytrong Graph@#, sau đó thay thế danh sách trống bằng danh sách các vòng lặp tự. Các chu kỳ được biểu diễn dưới dạng danh sách các cạnh và được sắp xếp theo độ dài, vì vậy chúng tôi thực hiện chu trình cuối cùng như vậy, sau đó từ tất cả các cạnh của nó, chúng tôi lấy đối số đầu tiên để chúng tôi có được danh sách các đỉnh theo định dạng đầu ra được chỉ định.

Nếu chỉ Mathicala coi các vòng lặp là một chu kỳ dài 1( AcyclicGraphQ @ CycleGraph[1, DirectedEdges -> True]cho True, nghiêm túc), thì chúng ta có thể lưu các 26byte khác :

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

1
Bạn sẽ không cần MaximalByvì kết quả FindCycleđã được sắp xếp theo độ dài (yếu tố cuối cùng là dài nhất). Ngoài ra, đối số đầu tiên FindCyclecó thể là một danh sách \[DirectedEdge](thay vì a Graph). Thêm vào đó, bạn có thể sử dụng 2 byte ;;(= 1;;-1) thay vì 3-byte Alltrong Partđể tiết kiệm một byte. -22 byte (58 byte):(FindCycle[#,∞,All]/.{}->{Cases[#,v_v_]})[[-1,;;,1]]&
JungHwan Min

3

Haskell , 157 154 150 byte

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

Hãy thử trực tuyến!

Cảm ơn @Laikoni và @Zgrab đã lưu một loạt byte!

Đây là một chương trình rất không hiệu quả:

Hàm đầu tiên #lấy danh sách các đường dẫn l(danh sách các danh sách số) và cố gắng mở rộng các phần tử của lbằng cách thêm trước mọi cạnh có thể (danh sách có độ dài 2) gcho mỗi phần tử của l. Điều này chỉ xảy ra nếu yếu tố củal không phải là một chu kỳ nếu nút mới được chuẩn bị trước không được chứa trong phần tử của l. Nếu nó đã là một chu kỳ, chúng tôi không trả trước bất cứ điều gì ngoài việc thêm nó vào danh sách các đường dẫn mới, nếu chúng tôi có thể mở rộng nó, chúng tôi sẽ thêm đường dẫn mở rộng vào danh sách mới, nếu không chúng tôi sẽ không thêm nó vào danh sách mới .

Bây giờ hàm hliên tục cố gắng mở rộng các đường dẫn đó (bắt đầu bằng danh sách các cạnh) cho đến khi chúng ta đạt đến một điểm cố định, nghĩa là chúng ta không thể mở rộng bất kỳ đường dẫn nào nữa. Tại thời điểm này, chúng tôi chỉ có chu kỳ trong danh sách của chúng tôi. Sau đó, nó chỉ là một vấn đề chọn chu kỳ dài nhất. Rõ ràng các chu kỳ xuất hiện nhiều lần trong danh sách này vì mỗi vòng quay có thể có của một chu kỳ lại là một chu kỳ.


Bạn có thể thả dấu ngoặc đơn vào (p:q)<-l.
Laikoni

Và sử dụng <$>thay vì mapnên lưu một byte khác ((,)=<<length)<$>[]:.
Laikoni

@Laikoni Cảm ơn bạn rất nhiều!
flawr

Bạn có thêm một không gian sau dòng cuối cùng. Ngoài ra, làm d@(p:q)<-ltiết kiệm một số byte.
Zgarb

Oh, d@(p:q)thực sự là tốt đẹp, cảm ơn bạn đã cho tôi thấy!
flawr

2

Bình thường, 20 byte

eMefqhMT.>{eMT1s.pMy

Bộ kiểm tra

Đưa ra một danh sách các cạnh, như trong các ví dụ.

Giải trình:

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 byte

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 thực hiện tất cả các công việc nặng, nhưng định dạng đầu ra của nó khá độc đáo và nó không phát hiện các chu kỳ có độ dài 1. Lưu ý rằng điều này không hoạt động với GNU tsort.

xác minh

--- 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 byte

Đã lưu 5 byte nhờ @Neil

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

Kiểm tra đoạn


Chắc chắn chuyển sang một cái cũ đơn giản mapgiúp bạn tiết kiệm một vài byte?
Neil

@Neil Nó phải .filter().map()như vậy, nên gần như chắc chắn là không. Công tắc đã tiết kiệm cho tôi 10 byte (mặc dù nó không được chơi đầy đủ như bây giờ)
Sản phẩm ETH

Tôi không thấy bạn sử dụng kết quả của sự hiểu biết, vì vậy thay vì sử dụng a.filter(z=>!e).map(z=>d)bạn có thể sử dụng a.map(z=>e?0:d).
Neil

Bạn nói đúng, tôi có thể kết hợp mọi thứ để tiết kiệm 5 byte. Và tôi chỉ nhận ra rằng mình cũng không cần a+a?:-)
ETHproductions

Downvoter có thể giải thích những gì sai? Liệu nó tạo ra đầu ra không chính xác?
Sản phẩm ETH

2

Haskell , 109 108 byte

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])]

Một giải pháp vũ lực: tạo ra tất cả các danh sách các cạnh có độ dài tăng dần cho đến khi độ dài của đầu vào, giữ các cạnh đó là chu kỳ, trả về cái cuối cùng. Đưa biểu đồ theo định dạng [(1,2),(2,3),(2,4),(4,1)]. Hãy thử trực tuyến!

Giải trình

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.

Phải mất một lúc cho đến khi tôi cuối cùng cũng hiểu chuyện gì đang xảy ra, phần kiểm tra đường đi / chu kỳ thực sự rất thông minh, tôi rất ngạc nhiên!
flawr

@flawr Cảm ơn! Chà, có vẻ như isaacg đã sử dụng thuật toán tương tự trước tôi.
Zgarb

0

MATLAB, 291 260 byte

Mất một ma trận adjecency Anơi một cạnh (i,j)được ký hiệu bằng một 1trong A(i,j), và Alà zero trong tất cả các mục khác. Đầu ra là một danh sách của một chu kỳ dài nhất. Danh sách trống nếu không có chu kỳ nào cả, và danh sách bao gồm điểm bắt đầu và điểm cuối nếu có chu kỳ. Nó sử dụng 1lập chỉ mục dựa trên.

Giải pháp này không sử dụng bất kỳ chức năng tích hợp nào liên quan đến biểu đồ.

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

Thật không may, điều này không chạy trong TryItOnline vì nó sử dụng một hàm trong một hàm, là đệ quy. Một chút sửa đổi cho phép bạn dùng thử trên octave-online.net .

Đối với trường hợp thử nghiệm cuối cùng, tôi đã tìm thấy một chu kỳ dài nhất thay thế [0 2 1 4 3 5 7 8 9 11 10 6 0] (ký hiệu này sử dụng lập chỉ mục dựa trên 0)

Giải trình

Cách tiếp cận cơ bản ở đây là chúng tôi thực hiện BFS từ mọi nút và lưu ý rằng chúng tôi không truy cập bất kỳ nút trung gian nào nữa ngoại trừ nút bắt đầu. Với ý tưởng đó, chúng tôi có thể thu thập tất cả các chu kỳ có thể, và dễ dàng chọn một chu kỳ dài nhất.

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.