Найдовший цикл у графіку


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 Ні, це просто збіг із тестовими випадками. Вихід повинен починатися (і необов'язково закінчуватися) першим вузлом циклу.
Мего

Вам слід вибрати формат, з кінцевою точкою або без - довільним і нічого не додає до виклику.
Чарівний восьминога Урна

5
@carusocomputing Я не згоден. Останній вузол неявний, якщо його відключити (оскільки він такий же, як перший вузол). Дозвіл на вибір того, повторити перший вузол чи ні, дозволяє отримати більше свободи в гольфі.
Мего

Відповіді:


4

Математика, 80 58 байт

Збережено колосальні 22 байти завдяки JungHwan Min

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

являє собою три байти символу приватного використання, що U+F3D5представляє \[DirectedEdge]. Чистою функцією з першим аргументом #очікується список спрямованих ребер. Знаходить Allцикли довжини не більше ніж InfinityуGraph@# , а потім замінює порожній список на список самоплів. Цикли представлені у вигляді списків ребер і відсортовані за довжиною, тому ми беремо останній такий цикл, потім з усіх його ребер беремо перший аргумент, щоб ми отримали список вершин у вказаному вихідному форматі.

Якщо лише Mathematica розглядає петлі як цикл довжини 1( AcyclicGraphQ @ CycleGraph[1, DirectedEdges -> True]дає True, серйозно), то ми могли б зберегти ще один 26байт:

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

1
Вам не знадобиться, MaximalByоскільки результат FindCycleвже відсортований за довжиною (останній елемент найдовший). Також першим аргументом FindCycleможе бути список \[DirectedEdge](замість а Graph). Крім того , ви можете використовувати 2-байти ;;(= 1;;-1) замість 3 х байт Allв , Partщоб зберегти байти. -22 байт (58 байт):(FindCycle[#,∞,All]/.{}->{Cases[#,v_v_]})[[-1,;;,1]]&
JungHwan Min

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.
Лайконі

А використання <$>замість mapмає зберегти ще один байт у ((,)=<<length)<$>[]:.
Лайконі

@Laikoni Дуже дякую!
невдача

Після фінального рядка у вас є додатковий простір. Крім того, це d@(p:q)<-lекономить деякі байти.
Згарб

О, 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 байт

Збережено 5 байт завдяки @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

Фрагмент тесту


Невже перехід на звичайний старий 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

Haskell , 109 108 байт

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 Дякую! Що ж, схоже, що перед мною isaacg використовував фактично той самий алгоритм.
Згарб

0

MATLAB, 291 260 байт

Бере матрицю прикріплення, Aде ребро (i,j)позначається символом 1in 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.