Гамильтоновы графы и сложность отыскания гамильтоновых циклов

Федеральное агентство по образованию РФ

		САРАТОВСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ

		ИМЕНИ Н.Г. ЧЕРНЫШЕВСКОГО

		Кафедра геометрии

		

		

		

		

		

		

		Гамильтоновы графы и сложность отыскания гамильтоновых циклов

		КУРСОВАЯ РАБОТА

		

		

		

		

		

		

		

		Научный руководитель

		Старший преподаватель ______________ 

		должн., уч. степень, уч. зван. подпись, дата инициалы, фамилия

		

		

		

		

		

		

		Саратов 2010

		
Содержание

		

			Введение

			Гамильтоновы графы

			1.1 Основные определения и результаты

			1.2 Теоремы достаточности гамильтонова графа

			Методы отыскания гамильтоновых циклов

			2.1 Алгебраические методы

			2.2 Метод перебора Робертса и Флореса

			2.2.1 Улучшение метода Робертса и Флореса

			Приложение

			Заключение

			Список литературы

		

		
Введение

		

		Целью моей курсовой работы является: 

		Ознакомление с основными понятиями, связанными с гамильтоновыми графами и циклами.

		Рассмотреть задачи и методы отыскания гамильтоновых циклов в графах

		3. Создание программы для нахождения гамильтоновых циклов.

		Прежде всего, чтобы внести ясность и уточнить терминологию, хотелось бы дать определения некоторым элементам графа таким, как маршрут, цепь, цикл.

		Маршрутом в графе G(V,E) называется чередующаяся последовательность вершин и ребер: ,, … , , в которой любые два соседних элемента инцидентны. Если  = , то маршрут замкнут, иначе открыт.

		Если все ребра различны, то маршрут называется цепью. Если все вершины (а значит, ребра) различны, то маршрут называется простой цепью.

		Замкнутая цепь называется циклом; замкнутая простая цепь называется простым циклом. Граф без циклов называется ациклическим. Для орграфов цепь называется путем, а цикл — контуром.

		

		
1. Гамильтоновы графы

		

		1.1 Основные определения и результаты

		

		

		

		Название «гамильтонов цикл» произошло от задачи «Кругосветное путешествие» предложенной ирландским математиком Вильямом Гамильтоном в 1859 году. Нужно было, выйдя из исходной вершины графа, обойти все его вершины и вернуться в исходную точку. Граф представлял собой укладку додекаэдра, каждой из 20 вершин графа было приписано название крупного города мира.

		Если граф имеет простой цикл, содержащий все вершины графа по одному разу, то такой цикл называется гамильтоновым циклом, а граф называется гамильтоновым графом. Граф, который содержит простой путь, проходящий через каждую его вершину, называется полугамильтоновым. Это определение можно распространить на ориентированные графы, если путь считать ориентированным.

		Гамильтонов цикл не обязательно содержит все ребра графа. Ясно, что гамильтоновым может быть только связный граф и, что всякий гамильтонов граф является полугамильтоновым. Заметим, что гамильтонов цикл существует далеко не в каждом графе.

		Замечание. 

		Любой граф G можно превратить в гамильтонов граф, добавив достаточное количество вершин. Для этого, например, достаточно к вершинам v1,…, vp графа G добавить вершины u1, …, up и множество ребер {(vi, ui)}  {(ui, vi+1)}.

		Степенью вершины v называется число ребер d(v), инцидентных ей, при этом петля учитывается дважды. В случае ориентированного графа различают степень do(v) по выходящим дугам и di(v) — по входящим.

		В отличии от эйлеровых графов, где имеется критерий для графа быть эйлеровым, для гамильтоновых графов такого критерия нет. Более того, задача проверки существования гамильтонова цикла оказывается NP-полной. Большинство известных теорем имеет вид: «если граф G имеет достаточное количество ребер, то граф является гамильтоновым». Приведем несколько таких теорем.

		

		1.2 Теоремы достаточности гамильтонова графа

		

		Теорема (Дирак, 1952) 1. Если в простом графе с n ≥ 3 вершинами p(v) ≥ n/2 для любой вершины v, то граф G является гамильтоновым. 

		Замечание Существует несколько доказательств этой широко известной теоремы, здесь мы приводим доказательство Д. Дж. Ньюмана.

		Доказательство. Добавим к нашему графу k новых вершин, соединяя каждую из них с каждой вершиной из G. Будем предполагать, что k — наименьшее число вершин, необходимых для того, чтобы полученный граф Gстал гамильтоновым. Затем, считая, что k > 0, придем к противоречию.

		Пусть v→p→w→…→v гамильтонов цикл в графе G, где v, w— вершины из G, а p— одна из новых вершин. Тогда w не является смежной с v, так как в противном случае мы могли бы не использовать вершину p, что противоречит минимальности k. Более того, вершина, скажем, w, смежная вершине w, не может непосредственно следовать за вершиной v, смежной вершине v, потому что тогда мы могли бы заменить v→p→w→…→v→ w→v на v→v→…→w→w→…→v, перевернув часть цикла, заключенную между w и v. Отсюда следует, что число вершин графа G, не являющихся смежными с w, не меньше числа вершин, смежных с v (то есть равно, по меньшей мере, n/2 + k); с другой стороны, очевидно, что число вершин графа G, смежных с w, тоже равно, по меньшей мере, n/2 + k. А так как ни одна вершина графа Gне может быть одновременно смежной и не смежной вершине w, то общее число вершин графа G, равное n + k, не меньше, чем n + 2k. Это и есть искомое противоречие. 

		Теорема (Оре) 2. Если число вершин графа G(V, E) n ≥ 3 и для любых двух несмежных вершин u и v выполняется неравенство:

		d(u) + d(v) ≥ n и (u, v)E, то граф G — гамильтонов.

		Граф G имеет гамильтонов цикл если выполняется одно из следующих условий: 

		Условие Бонди: из d(vi) ≤ i и d(vk) ≤ k => d(vi) + d(vk) ≥ n (k ≠ i)

		Условие Хватала: из d(vk) ≤ k ≤ n/2 => d(vn-k) ≥ n  k.

		Далее, известно, что почти все графы гамильтоновы, то есть

		

		

		где H(p) — множество гамильтоновых графов с p вершинами, а G(p) — множество всех графов с p вершинами. Задача отыскания гамильтонова цикла или эквивалентная задача коммивояжера являются практически востребованными, но для нее неизвестен (и, скорее всего не существует) эффективный алгоритм решения.

		Пример графа, когда не выполняется условие теоремы Дирака, но граф является гамильтоновым.

		
 

		N = 8; d(vi) = 3; 3 ≤ 8/2 = 4 не гамильтонов граф, но существует гамильтонов цикл: M = (1, 2, 3, 4, 5, 6, 7, 8, 1) 




2. Методы отыскания гамильтоновых циклов



2.1 Алгебраические методы

		

		Пока неизвестно никакого простого критерия или алгебраического метода, позволяющего ответить на вопрос, существует или нет в произвольном графе G гамильтонов цикл. Критерии существования, данные выше, представляют теоретический интерес, но являются слишком общими и не пригодны для произвольных графов, встречающихся на практике. Алгебраические методы определения гаильтоновых циклов не могут быть применены с более чем несколькими десятками вершин, так как они требуют слишком большого времени работы и большой памяти компьютера. Более приемлемым является способ Робертса и Флореса, который не предъявляет чрезмерных требований к памяти компьютера, но время в котором зависит экспоненциально от числа вершин в графе. Однако другой неявный метод перебора имеет для большинства типов графов очень небольшой показатель роста времени вычислений в зависимости от числа вершин. Он может быть использован для нахождения гамильтоновых циклов в очень больших графах. Этот метод включает в себя построение всех простых цепей с помощью последовательного перемножения матриц. «Внутреннее произведение вершин» цепи x1, x2, … , xk-1, xk определяется как выражение вида x2 * x3 * … xk-1, не содержащее две концевые вершины x1 и xk. «Модифицированная матрица смежности» B = [β(i, j)] — это (n × n)- матрица, в которой β(i, j) — xj, если существует дуга из xi в xj и нуль в противном случае. Предположим теперь, что у нас есть матрица PL = [pL(i, j)], где pL(i, j) — сумма внутренних произведений всех простых цепей длины L (L ≥ 1) между вершинами xi и xj для xi ≠ xj. Положим pL(i, i)=0 для всех i. Обычное алгебраическое произведение матриц B * PL = P’L+1 = [p’L+1(s, t)] определяется как т.е. p’L+1(s, t) является суммой внутренних произведений всех цепей из xs в xt длины l+1. Так как все цепи из xk в xt, представленные внутренними произведениями из pL(k, t), являются простыми, то среди цепей, 

		

		

  kp1+1(s, t) = ∑(s, k) * p1(k, t)

		

		получающихся из указанного выражения, не являются простыми лишь те, внутренние произведения которых в pL(k, t) содержат вершину xs. Таким образом, если из p’L+1(s, t) исключить все слагаемые, содержащие xs (а это можно сделать простой проверкой), то получим pL+1(s, t). Матрица PL+1 = [pL + 1(s, t)], все диагональные элементы которой равны 0, является тогда матрицей всех простых цепей длины L + 1.

		Вычисляя затем B * PL+1, находим PL+2 и т.д., пока не будет построена матрица Pn-1, дающая все гамильтоновы цепи (имеющие длину n  1) между всеми парами вершин. Гамильтоновы циклы получаются тогда сразу из цепей в Pn-1 и тех дуг из G, которые соединяют начальную и конечную вершины каждой цепи. С другой стороны, гамильтоновы циклы даются членами внутреннего произведения вершин, стоящими в любой диагональной ячейке матрицы B * Pn-1 (все диагональные элементы этой матрицы одинаковые).

		Очевидно, что в качестве начального значения матрицы P (т.е. P1) следует взять матрицу смежности A графа, положив все ее диагональные элементы равными нулю.

		Недостатки этого метода совершенно очевидны. В процессе умножения матриц (т.е. когда L увеличивается) каждый элемент матрицы PL будет состоять из все большего числа членов вплоть до некоторого критического значения L, после которого число членов снова начнет уменьшаться. Это происходит вследствие того, что для малых значений L и для графов, обычно встречающихся на практике, число цепей длины L + 1, как правило, больше, чем число цепей длины L, а для больших значений L имеет место обратная картина. Кроме того, так как длина каждого члена внутреннего произведения вершин увеличивается на единицу, когда L увеличивается на единицу, то объем памяти, необходимый для хранения матрицы PL, растет очень быстро вплоть до максимума при некотором критическом значении L, после которого этот объем снова начинает уменьшаться.

		Небольшая модификация вышеприведенного метода позволяет во много раз уменьшить необходимый объем памяти и время вычислений. Так как нас интересуют только гамильтоновы циклы и, как было отмечено выше, они могут быть получены из членов внутреннего произведения любой диагональной ячейки матрицы B * Pn-1, то необходимо знать только элемент pn-1(1, 1). При этом на каждом этапе не обязательно вычислять и хранить всю матрицу PL, достаточно лишь найти первый столбец PL. Эта модификация уменьшает необходимый объем памяти и время вычислений в n раз. Однако даже при использовании этой модификации программа для ЭВМ, написанная на языке PL/1, который позволяет построчную обработку литер и переменное распределение памяти, не была способна найти все гамильтоновы циклы в неориентированных графах с более чем примерно 20 вершинами и средним значением степени вершины, большим 4. Использовался компьютер IBM 360/65 с памятью 120 000 байтов. Более того, даже для графа с вышеуказанными «размерами» данный метод использовал фактически весь объем памяти и время вычислений равнялось 1.8 минуты. Не такое уж незначительное время для столь небольшого графа.

		

		2.2 Метод перебора Робертса и Флореса

		

		В противоположность алгебраическим методам, с помощью которых пытаются найти сразу все гамильтоновы циклы и при реализации которых приходится хранить, поэтому все цепи, которые могут оказаться частями таких циклов, метод перебора имеет дело с одной цепью, непрерывно продлеваемой вплоть до момента, когда либо получается гамильтонов цикл, либо становится ясно, что эта цепь не может привести к гамильтонову циклу. Тогда цепь модифицируется некоторым систематическим способом (который гарантирует, что, в конце концов, будут исчерпаны все возможности), после чего продолжается поиск гамильтонова цикла. В этом способе для поиска требуется очень небольшой объем памяти и за один раз находится один гамильтонов цикл.

		Следующая схема перебора, использующая обычную технику возвращения, была первоначально предложена Робертсом и Флоресом. Начинают с построения (k × n)-матрицы M=[mij], где элемент mij есть i-я вершина (скажем xq), для которой в графе G(X, Г) существует дуга (xj, xq). Вершины xq во множестве Г(xj) можно упорядочить произвольно, образовав элементы j-го столбца матрицы M. Число строк k матрицы M будет равно наибольшей полустепени исхода вершины.

		Метод состоит в следующем. Некоторая начальная вершина (скажем, x1) выбирается в качестве отправной и образует первый элемент множества S, которое каждый раз будет хранить уже найденные вершины строящейся цепи. К S добавляется первая вершина (например, вершина a) в столбце x1. Затем к множеству S добавляется первая возможная вершина (например, вершина b) в столбце a, потом добавляется к S первая возможная вершина (например, вершина c) в столбце b и т.д. Под «возможной» вершиной мы понимаем вершину, еще не принадлежащую S. Существуют две причины, препятствующие включению некоторой вершины на шаге r во множество S = {x1, a, b, c, … , xr-1, xr}:

		В столбце xr нет возможной вершины.

		Цепь, определяемая последовательностью вершин в S, имеет длину n  1, т.е. является гамильтоновой цепью. 

		В случае 2) возможны следующие случаи:

		В графе G существует дуга (xr, x1), и поэтому найден гамильтонов цикл.

		Дуга (xr, x1) не существует и не может быть получен никакой гамильтонов цикл.

		В случаях (1) и (2b) следует прибегнуть к возвращению, в то время как в случае (2a) можно прекратить поиск и напечатать результат (если требуется найти только один гамильтонов цикл), или (если нужны все такие циклы) произвести печать и прибегнуть к возвращению.

		Возвращение состоит в удалении последней включенной вершины xr из S, после чего остается множество S = {x1, a, b, c, … , xr-1}, и добавлении к S первой возможной вершины, следующей за xr, в столбце xr-1 матрицы M. Если не существует никакой возможной вершины, делается следующий шаг возвращения и т.д. 

		Поиск заканчивается в том случае, когда множество S состоит только из вершины x1 и не существует никакой возможной вершины, которую можно добавить к S, так что шаг возвращения делает множество S пустым. Гамильтоновы циклы, найденные к этому моменту, являются тогда всеми гамильтоновыми циклами, существующими в графе.

		Рассмотрим пример поиска гамильтонова цикла в графе переборным методом Робертса и Флореса.

		Пример:

		

		

		

		"2"

		1) S = {1}

		2) S = {1, 2} 

		3) S = {1, 2, 3} 

		4) S = {1, 2, 3, 4} 

		5) S = {1, 2, 3, 4, 5} - Г 4→3 4→5

		6) S = {1, 2, 3, 4}

		7) S = {1, 2, 3}  3→1 3→2 3→4

		8) S = {1, 2} 

		9) S = {1}

		"3"

		10) S = {1, 3}  3→2

		11) S = {1, 3, 2} 2→1

		12) S = {1, 3} 2→3

		13) S = {1, 3, 4} 3→4 4→5

		14) S = {1, 3, 4, 5, 4} 5→нет

		15) S = {1, 3, 4}

		16) S = {1, 3}

		17) S = {1}

		"5"

		18) S = {1, 5}

		19) S = {1, 5, 4}

		20) S = {1, 5, 4, 3}

		21) S = {1, 5, 4, 3, 2} - Г

		22) S = {1, 5, 4, 3}

		23) S = {1, 5, 4}

		24) S = {1, 5}

		25) S = {1}

		26) S = Ø



2.2.1 Улучшение метода Робертса и Флореса

		Рассмотрим улучшение основного переборного метода Робертса и Флореса. Допустим, что на некотором этапе поиска построенная цепь задается множеством S = {x1, x2, … , xr} и что следующей вершиной, которую предполагается добавить к S, является x*S. Рассмотрим теперь две следующие ситуации, в которых вершина является изолированной в подграфе, остающемся после удаления из G(X,Г) всех вершин, образующих построенную до этого цепь.

		Если существует такая вершина xX\S, что xГ(xr) и Г-1(x)  S, то, добавляя к S любую вершину x*, отличную от x, мы не сможем в последующем достигнуть вершины x ни из какой конечной вершины построенной цепи, и, значит, эта цепь не сможет привести нас к построению гамильтонова цикла. Таким образом, в этом случае x является единственной вершиной, которую можно добавить к S для продолжения цепи.

		Если существует такая вершина xX\S, что xГ-1(x1) и Г(x) S{x*} для некоторой другой вершины x*, то x* не может быть добавлена к S, так как тогда в остающемся подграфе не может существовать никакой цепи между x и x1. Цепь, определяемая множеством S  {x*}, не может поэтому привести к гамильтонову циклу, а в качестве кандидата на добавление к множеству S следует рассмотреть другую вершину, отличную от x*.

		Проверка условий (a) и (b) будет, конечно, замедлять итеративную процедуру, и для небольших графов (менее чем с 20 вершинами) не получается никакого улучшения первоначального алгоритма Робертса и Флореса. Но для больших графов эта проверка приводит к заметному сокращению необходимого времени вычислений, уменьшая его обычно в 2 или более раз.

		

		
Заключение

		

		В данной работе мы познакомились с основными понятиями, определениями и результатами, связанными с гамильтоновыми графами и циклами. Так же мы рассмотрели ту часть теории сложности, которая затрагивает задачи отыскания гамильтонова цикла. И в итоги проведённых изучений была написана программа отыскания гамильтонова цикла в графе.

		

		
Список литературы

			

			Кристофидес Н. Теория графов. Алгоритмический подход. -М.: Мир, 1978.-432с.

			Белов В.В. Теория графов: Учеб. пособие для студ.высш.техн.учеб. заведений.-М.: Высш.школа, 1976.-392с.

			Культин Н.Б. Программирование в Turbo Pascal 7.0 и Delphi.- Санкт-Петербург:BHV, 1998.-240c.

			В.М. Бондарев, В.И. Рублинецкий, Е.Г. Качко. Основы программирования, 1998 г.

			Ф.А. Новиков. Дискретная математика для программистов, Питер, 2001 г.

			В.А. Носов. Комбинаторика и теория графов, МГТУ, 1999 г.

			О. Оре. Теория графов, Наука, 1982 г.

			www.codenet.ru

			www.algolist.ru

		

		
Приложение

		

		Программа отыскания гамильтонова цикла в графе:

		

			Uses

			dos,crt;

			VAR a,b:array[1..100,1..100] of integer;

			i,j,n,ij:integer;

			stro:text; 

			procedure ini_b; //модифицирование матрицы смежности (из А создаем В)

			var i,j:integer;

			begin;

			for i:=1 to n do

			for j:=1 to n do

			b[i,j]:=a[i,j]*j;

			end;

			procedure ini_p1; // Формирование матрицы  из А

			var i,j:integer;

			s_i,s_j:string[3];

			f1:text;

			begin;

			for i:=1 to n do

			for j:=1 to n do

			begin;

			str(i,s_i); if i<10 then s_i:='00'+s_i else if i<100 then s_i:='0'+s_i;

			str(j,s_j); if j<10 then s_j:='00'+s_j else if j<100 then s_j:='0'+s_j;

			assign(f1,'vrm\p'+s_i+s_j+'.txt');

			rewrite(f1);

			if a[i,j]<>0 then writeln(f1,a[i,j]:4);

			close(f1);

			end;

			end;

			procedure multi_B_P1(nom:integer); //перемножение матриц  и В, 

			запись результата в 

			var ii,i,j,k,s,ip:integer;

			s_i,s_j,s_k:string[3];

			f1,f2:text;

			label q1;

			begin;

			for i:=1 to n do

			for j:=1 to n do

			begin;

			str(i,s_i); if i<10 then s_i:='00'+s_i else if i<100 then s_i:='0'+s_i;

			str(j,s_j); if j<10 then s_j:='00'+s_j else if j<100 then s_j:='0'+s_j;

			assign(f2,'vrm\p2'+s_i+s_j+'.txt');

			rewrite(f2);

			if i<>j then begin;

			for k:=1 to n do

			begin;

			str(k,s_k); if k<10 then s_k:='00'+s_k else if k<100 then s_k:='0'+s_k;

			if (b[i,k]=i) or (b[i,k]=j) or (b[i,k]=0) then goto q1;

			assign(f1,'vrm\p'+s_k+s_j+'.txt');

			reset(f1);

			ii:=0;

			ip:=0;

			while not eof(f1) do begin;

			ip:=0;ii:=0;

			while not eoln(f1) do begin;

			ip:=0;

			read(f1,ip);

			if (nom=1) and (ip<>0) then begin; {write(f2,ip:4);}ii:=2;end;

			if (nom>1) and (ip<>0)then begin;

			if ii=0 then begin;write(f2,b[i,k]:4);ii:=1;end; write(f2,ip:4);end;

			end;

			if ii=2 then begin;write(f2,b[i,k]:4);end;

			if ii>0 then writeln(f2);

			ii:=0;

			readln(f1);

			end;

			close(f1);

			q1: end;

			end;

			close(f2);

			end;

			end;

			procedure rename_P1_P2; // теперь нам  уже не требуется и ей присваиваются //значения , в свою очередь в будут записаны новые данные при втором проходе

			var i,j,IP1,IP2:integer;

			s_i,s_j:string[3];

			f1,F2:text;

			AA:ARRAY[1..100] OF INTEGER;

			ia,k,li,llj:integer;

			label mm,mm2;

			begin;

			for i:=1 to n do

			for j:=1 to n do

			begin;

			str(i,s_i); if i<10 then s_i:='00'+s_i else if i<100 then s_i:='0'+s_i;

			str(j,s_j); if j<10 then s_j:='00'+s_j else if j<100 then s_j:='0'+s_j;

			assign(f1,'vrm\p'+s_i+s_j+'.txt');

			erase(f1);

			assign(f1,'vrm\p2'+s_i+s_j+'.txt');

			reset(f1);

			assign(f2,'vrm\p'+s_i+s_j+'.txt');

			rewrite(f2);

			ia:=1; llj:=0;

			while not eof(f1) do begin;

			ia:=1;

			while not eoln(f1) do begin;

			read(f1,aa[ia]); inc(ia);

			end;

			if ia=1 then goto mm;

			dec(ia);

			for k:=1 to ia do if (aa[k]=0) or (aa[k]=i) or (aa[k]=j) then goto mm;

			for k:=1 to ia do begin;

			for li:=1 to k-1 do if (aa[k]=aa[li]) then goto mm2;

			write(f2,aa[k]:4);llj:=1; mm2:end;

			mm: readln(f1);if llj>0 then writeln(f2);

			end;

			close(f1);close(f2);

			erase(f1);

			end;

			end;

			procedure viv_P; // процедура использовалась при отладке программы,

			отвечала за вывод на экран промежуточных матриц и 

			var ii,jj,i,j,k,s,ip:integer;

			s_i,s_j:string[3];

			f1:text;

			begin;

			clrscr;

			for i:=1 to n do

			for j:=1 to n do

			begin;

			str(i,s_i); if i<10 then s_i:='00'+s_i else if i<100 then s_i:='0'+s_i;

			str(j,s_j); if j<10 then s_j:='00'+s_j else if j<100 then s_j:='0'+s_j;

			write('p[',i,',',j,']=');

			assign(f1,'vrm\p'+s_i+s_j+'.txt');

			reset(f1);

			ii:=0;jj:=0;

			while not eof(f1) do begin;

			while not eoln(f1) do begin;

			read(f1,ip);

			if (ii=0) and (jj>0) then write('+');

			if ii>0 then write('*'); write(ip);ii:=1;

			end;

			readln(f1); jj:=1; II:=0;

			end;

			readln;

			close(f1);

			end;

			end;

			

			procedure viv_P2; // запись в файл example.txt промежуточных матриц

			var ii,jj,i,j,k,s,ip:integer;

			s_i,s_j:string[3];

			f1:text;

			begin;

			writeln(stro,'*********************************************');

			for i:=1 to n do

			for j:=1 to n do

			begin;

			str(i,s_i); if i<10 then s_i:='00'+s_i else if i<100 then s_i:='0'+s_i;

			str(j,s_j); if j<10 then s_j:='00'+s_j else if j<100 then s_j:='0'+s_j;

			write(stro,'p[',i,',',j,']=');

			assign(f1,'vrm\p'+s_i+s_j+'.txt');

			reset(f1);

			ii:=0;jj:=0;

			while not eof(f1) do begin;

			while not eoln(f1) do begin;

			read(f1,ip);

			if (ii=0) and (jj>0) then write(stro,'+');

			if ii>0 then write(stro,'*'); write(stro,ip);ii:=1;

			end;

			readln(f1); jj:=1; II:=0;

			end; writeln(stro);

			close(f1);

			end;

			end;

			procedure viv_res; // изначально использовалась для вывода результатов на экран

			var ii,jj,i,j,k,s,ip,iij:integer;

			ss_i,ss_j, s_i,s_j:string[3];

			f1:text;

			begin;

			clrscr;

			for i:=1 to n do

			for j:=1 to n do

			begin;

			str(i,s_i); if i<10 then s_i:='00'+s_i else if i<100 then s_i:='0'+s_i;

			str(j,s_j); if j<10 then s_j:='00'+s_j else if j<100 then s_j:='0'+s_j;

			str(j,ss_j);

			str(i,ss_i);

			assign(f1,'vrm\p'+s_i+s_j+'.txt');

			reset(f1);

			ii:=0;jj:=0;iij:=0;

			while not eof(f1) do begin;

			while not eoln(f1) do begin;

			read(f1,ip);

			if (ii=0) and (jj>0) then begin;write(' '); iij:=0;end;

			if ii>0 then write('-');

			if iij=0 then begin;

			write('CHAIN p[',i,',',j,']=',ss_j,'-',ss_i,'-');

			iij:=1;

			end;

			write(ip);ii:=1;

			end;

			readln(f1); jj:=1; II:=0;

			end;

			if iij>0 then readln;

			close(f1);

			end;

			end;

			

			procedure delete_povtor; // удаление повторов и вывод результатов

			var ii,jj,i,j,k,s,ip,iij:integer;

			s_i,s_j:string[3];

			f1:text;

			et1:array[1..100,0..100] of integer;

			kol_et,i3:integer;

			function prov_povtor:boolean; // непосредственно проверка на повторы

			var iaa,k2,l,l2:integer;

			label ddd,ddd2;

			begin;

			for k2:=1 to et1[i,0]-1 do

			if et1[i,k2]<>et1[j,k2] then goto ddd;

			prov_povtor:=true;exit;

			ddd:

			for l:=1 to et1[i,0]-1 do

			begin;

			iaa:=et1[i,1];

			for l2:=2 to et1[i,0]-1 do et1[i,l2-1]:=et1[i,l2];

			et1[i,et1[i,0]-1]:=iaa;

			for k2:=1 to et1[i,0]-1 do

			if et1[i,k2]<>et1[j,k2] then goto ddd2;

			prov_povtor:=true;exit;

			ddd2:

			end;

			prov_povtor:=false;exit;

			end;

			label yyy;

			begin;

			kol_et:=1; s:=0;

			for i:=1 to 100 do et1[i,0]:=1;

			for i:=1 to n do

			for j:=1 to n do

			begin;

			str(i,s_i); if i<10 then s_i:='00'+s_i else if i<100 then s_i:='0'+s_i;

			str(j,s_j); if j<10 then s_j:='00'+s_j else if j<100 then s_j:='0'+s_j;

			assign(f1,'vrm\p'+s_i+s_j+'.txt');

			reset(f1);

			while not eof(f1) do begin;

			ii:=0;

			while not eoln(f1) do begin;

			read(f1,ip);

			if ip>0 then begin;

			if ii=0 then begin;

			et1[kol_et,et1[kol_et,0]]:=j;

			inc(et1[kol_et,0]);

			et1[kol_et,et1[kol_et,0]]:=i;

			inc(et1[kol_et,0]);

			ii:=1;end;

			et1[kol_et,et1[kol_et,0]]:=ip;

			inc(et1[kol_et,0]);

			end;

			end;

			readln(f1); ii:=0;

			if (a[et1[kol_et,et1[kol_et,0]-1],et1[kol_et,1]]=1) and

			(a[et1[kol_et,1],et1[kol_et,2]]=1) then inc(kol_et);

			end;

			close(f1);

			end;

			for i:=1 to kol_et-1 do begin;

			for j:=1 to i-1 do begin;

			if prov_povtor then goto yyy;

			end;

			if s=0 then begin 

			writeln;

			writeln('Найденные пути:');end;

			writeln;

			s:=1; // вывод найденных путей

			for k:=1 to et1[i,0]-1 do write(et1[i,k],'-'); write(et1[i,1]);

			yyy: end;

			if s=0 then writeln('Нет решения');

			{ for i:=1 to kol_et-1 do begin;

			writeln;

			for j:=1 to et1[i,0]-1 do write(et1[i,j],'-');

			end;}

			end;

			procedure delete_vrm; // удаление временных файлов

			var i,j:integer;

			s_i,s_j:string[3];

			f1:text;

			begin;

			for i:=1 to n do

			for j:=1 to n do

			begin;

			str(i,s_i); if i<10 then s_i:='00'+s_i else if i<100 then s_i:='0'+s_i;

			str(j,s_j); if j<10 then s_j:='00'+s_j else if j<100 then s_j:='0'+s_j;

			assign(f1,'vrm\p'+s_i+s_j+'.txt');

			erase(f1);

			end;

			end;

			BEGIN;

			clrscr;

			gotoxy(1,1);writeln('Программа поиска гамильтоновых циклов ');

			gotoxy(1,2);writeln('Введите количество вершин графа ');

			gotoxy(1,3);readln(n);

			if (n<3) or (n>100) then begin;writeln('Превышены возможности программы’);

			readkey;exit;end;

			gotoxy(1,4);writeln('Введите матрицу смежности графа');

			for i:=1 to n do begin

			for j:=1 to n do begin

			gotoxy(3*j,3+2*i+1);read(A[i,j]); // считывание матрицы А

			if not ((A[i,j]=0) or (A[i,j]=1)) then begin 

			writeln(' Превышены возможности программы’');readkey;exit;end;

			end;end;

			ini_B;

			ini_p1;

			assign(stro,'vrm\example.txt');

			rewrite(stro);

			for ij:=1 to n-2 do begin;

			multi_b_p1(ij);

			rename_p1_p2;

			viv_p2;

			end;

			close(stro);

			// viv_p;

			delete_povtor;

			delete_vrm;

			//viv_res;

			readkey;

			end.