Наибольшее парасочетание в двудольном графе.



Шуточная постановка задачи: имеется N мужчин и K женщин, каждый мужчина указывает несколько женщин на которых он согласен жениться. Мнение женщин не спрашивается. Заключить наибольшее количество моногамных браков.

В терминах теории графов это звучит так: Дан двудольный граф G содержащий N вершин и M ребер. Требуется найти наибольшее парасочетание, т.е. выбрать как можно больше ребер, чтобы ни одно выбранное ребро не имело общей вершины ни с каким другим выбранным ребром.

Алгоритм Куна

Необходимое определение: парасочетание M называется множество ребер не имеющих общих вершин. Мощностью парасочетания назовем число ребер в нем. Наибольшим парасочетанием назовем парасочетание мощность которого максимально среди всех возможных парасочетаний в данном графе. все вершины у которых есть смежное ребро из парасочетаний назовем насыщенным эти парасочетания. Цепью длины K назовем некоторый простой путь содержащий K ребер. Чередующийся цепью называется цепь в которой ребра поочередно принадлежат/не принадлежат к парасочетанию. Увеличивающей цепью называют цепь у которой начальная и конечные вершины не принадлежат парасочетанию.

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

Алгоритм Куна - это непосредственное применение теоремы Бержа. Его кратко можно описать так, сначала берем пустое парасочетание, а потом пока в графе удачается найти увеличивающую цепь будем выполнят чередование парасочетание вдоль этой цепи и повторят процесс поиска увеличивающей цепи, как только такую цепь не найдем - процесс останавливаем, текущая парасочетание будет максимальной.

Рассмотрим детализацию алгоритма. Алгоритм Куна ищет любую увеличивающийся цепь с помощью обхода глубину, или в ширины. Алгоритм рассматривает все вершины графа по очередью запуская из каждой вершины обход. Пытающийся найти увеличивающую цепь начинающийся в этой вершине. Удобнее описывать этот алгоритм считая что граф уже разбит на две доли. Алгоритм просматривает все вершины V первой доле графа. Если текущая вершина V уже насыщена текущим парасочетанием, то эту вершину пропускаем, иначе алгоритм пытается насытить эту вершину для чего запускается поиск увеличивающей цепи, начинающихся с этой вершины. При этом рассматриваем все ребра из этой вершины. Пусть текущая ребро (v,t0). Если вершина t0 еще ненасыщенная парасочетание то мы нашли увеличивающую цепь из одного ребра. В таком случае мы его просто включаем в парасочетание, и прекращаем поиск увеличивающей цепи вершины V, иначе если t0 уже насыщена каким-то ребром (t0,p), то идем вдоль этого ребра и попытаемся найти увеличивающую цепь. Проходящую через ребра (v,t0) и (to,p) и т.д.

В схеме ребро принадлежащее парасочетанию "сделает жирными", а другие ребра "тонкими".

Так как поиск глубину запускается с каждой вершины, а их N количество операций будет O(n*m), m- кол-во ребер(O(n^3)).

Рассмотрим реализацию алгорится.

n - число вершин в первой доле.

k- число вершин во второй доле.

g[v] - список ребер из вершины v;

mt - содержить информация о текущем парасочетании, причем mt[i] - это номер вершины первой доли связанный ребром с вершины i второй доли. Если mt[i] =-1 никакого ребра парасочетания из i невыходит.

used - обычный массив отметки посещения вершины в обходе глубину.

bool kuhn(int v){

int i,t;

if(!used[v]){

used[v]=true;

for(i=0; i<=ng[v];i++){

t=g[v][i];

if(mt[t0]==-1||kuhn(mt[t0])){

mt[t0]=v; return true;

}

}

}

return false;

}

void main(){

cin>>k>>m;

for(l=1;l<=m;l++){

cin>>i>>j;

ng[i]++; v=ng[i]; g[i][v]=j;

}

memset(mt, -1, sizeof(int));

for(v=1;v<=n;v++){

memset(used, false, sizeof(used));

kuhn(v);

}

for(i=1;i<=k;i++){

if(mt[i]!=-1) cout<<mt[i]<<' '<<i<<endl;

}

}


Дата добавления: 2016-01-06; просмотров: 39; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!