Раздел «Алгоритмы».BipartiteControllingSetCPP:

Поиск минимального контролирующего множества в двудольном графе: C++

/*
 * Поиск минимального контролирующего множества вершин в двудольном графе.
 * Даниил Швед, 2008. МФТИ.
 * danshved [no-spam] gmail.com
 */
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <algorithm>
using namespace std;

typedef vector<int> VInt;
typedef vector<VInt> VVInt;
typedef VInt::iterator VIter;
typedef vector<bool> VBool;

VVInt graph;
int leftCount, rightCount;
VInt match;
VBool visited;
VBool isRight;

// Обход в глубину для поиска увеличивающего чередующегося пути
bool matchVisit(int u) {
   visited[u] = true;
   for(VIter it = graph[u].begin(); it != graph[u].end(); it++)
      if(match[*it] == -1 || (!visited[match[*it]] && matchVisit(match[*it]))) {
         match[*it] = u;
         return true;
      }
   return false;
}

// Обход в глубину для переключения ребер паросочетания
void controlVisit(int u) {
   visited[u] = true;
   for(VIter it = graph[u].begin(); it != graph[u].end(); it++)
      if(!isRight[*it] && !visited[match[*it]]) {
         controlVisit(match[*it]);
         isRight[*it] = true;
      }
}

// Поиск наименьшего контролирующего множества
pair<VInt, VInt> control() {
   int i;
   
   // Найдем наибольшее паросочетание
   match.assign(rightCount, -1);
   visited.assign(leftCount, false);
   
   for(i = 0; i < leftCount; i++) {
      visited.assign(leftCount, false);
      matchVisit(i);
   }
   
   // Найдем свободные вершины в левой доле
   VBool isFree(leftCount, true);
   for(i = 0; i < rightCount; i++)
      if(match[i] != -1)
         isFree[match[i]] = false;

   // Запустим поиск в глубину из свободных вершин, в процессе поиска
   // переключим некоторые ребра из "левого" состояния в "правое".
   isRight.assign(rightCount, false);
   visited.assign(leftCount, false);
   for(i = 0; i < leftCount; i++)
      if(isFree[i])
         controlVisit(i);
         
   // Вернем ответ в виде пары массивов (контролирующие вершины в каждой из долей)
   pair<VInt, VInt> result;
   for(i = 0; i < rightCount; i++)
      if(match[i] != -1) {
         if(isRight[i])
            result.second.push_back(i);
         else
            result.first.push_back(match[i]);
      }
   return result;
}

// Демонстрация: считаем граф и выведем контролирующее множество
int main() {
   int edgeCount;
   
   scanf("%d%d%d", &leftCount, &rightCount, &edgeCount);
   graph.resize(leftCount);
   while(edgeCount--) {
      int from, to;
      scanf("%d%d", &from, &to);
      graph[from-1].push_back(to-1);
   }
   
   pair<VInt, VInt> answer = control();
   
   printf("In the left part: ");
   for(VIter it = answer.first.begin(); it != answer.first.end(); it++)
      printf("%d ", *it + 1);
   printf("\nIn the right part: ");
   for(VIter it = answer.second.begin(); it != answer.second.end(); it++)
      printf("%d ", *it + 1);
   printf("\n");
}

-- DanielShved - 23 Mar 2008