二分图匹配 与 匈牙利算法(Hungary)

今天开始继续学习啦~


二分图

二分图也称为二部图,定义如下:
设无向图G=,若能将V划分成V1,V2两个独立的顶点集(V1交V2为空,V1并V2为G,且V1,V2非空),使得G中每条边的两个端点皆是一个属于V1,另一个属于V2,则称G为二分图(二部图),V1,V2为互补的顶点子集,常将二部图G记作
若G是简单二分图,V1中的每个顶点均与V2中的所有顶点相邻,称G为完全二部图,记为Kr,s 其中r=|V1|,s=|V2|
n阶零图 (n>=2) 是二分图

定理:n阶无向图G是二分图当且仅当G中无奇圈

匹配

匹配:一个匹配是一个边的集合,集合里面的任意两条边都没有公共顶点
最大匹配:最大匹配即含边数最多的匹配
完美匹配:一个图中所有顶点皆为匹配点,则称其为一个完美匹配
交替路:从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边…形成的路径叫交替路
增广路:从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路。增广路径有以下性质(设图为G,P为两个未匹配点之间的路径,M为已匹配的边集:
(1)P的路径个数必定为奇数,第一条边和最后一条边都不属于M。
(2)将M和P进行取反操作可以得到一个更大的匹配
(3)M为G的最大匹配当且仅当不存在M的增广路径

匈牙利算法

算法描述:设图为G,P为两个未匹配点之间的路径,M为已匹配的边集
1.首先置M为空
2.找增广路径P,再进行异或操作得到更大的匹配
3.重复第2步直到找不出增广路径为止

算法模板

#include<iostream>
#include<string>
#include<string.h>
//description: this is a program for algorithm-Hungarian
const int MAXN = 101;
using std::string;
//
int n1,n2;//vertexes
int m;//edge
int Matrix[MAXN][MAXN];//Graph
int Visited[MAXN];//sign
int Match[MAXN];//indicate match
//
int DFS(int x)
{
 for(int i=1;i<=n1;i++)
 {
  if(!Visited[i]&&Matrix[x][i])//judge unvisited & matchable
  {
   Visited[i]=1;
   if(Match[i]==0||DFS(Match[i]))//judge (unmatched) or (have to change match point)
   {
    Match[i]=x;
    return 1;//match successful
   }
  }
 }
 return 0;
}//Hungarian Algorithm
//
int main()
{
 memset(Match,0,sizeof(Match));
 while(std::cin>>m>>n1>>n2)
 {
  memset(Matrix,0,sizeof(Matrix));
  int count=0;
  int row;
  int col;//axis
  for(int i=1;i<=m;i++)
  {
   std::cin>>row>>col;
   Matrix[row][col]=1;//connect
  }
  //Hungarian
  for(int i=1;i<=n2;i++)
  {
   memset(Visited,0,sizeof(Visited));
   if(DFS(i))
   {
    count++;
   }
  }
  //
  std::cout<<"output:"<<count<<std::endl;//output
 }
 return 0;
}

一般最大匹配的结果可能不止一种,所以大多数题目应该是输出最大匹配数


题目:

P2756 飞行员问题

英国皇家空军从沦陷国征募了大量外籍飞行员。由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的2 名飞行员,其中1 名是英国飞行员,另1名是外籍飞行员。在众多的飞行员中,每一名外籍飞行员都可以与其他若干名英国飞行员很好地配合。如何选择配对飞行的飞行员才能使一次派出最多的飞机。对于给定的外籍飞行员与英国飞行员的配合情况,试设计一个算法找出最佳飞行员配对方案,使皇家空军一次能派出最多的飞机。
对于给定的外籍飞行员与英国飞行员的配合情况,编程找出一个最佳飞行员配对方案,使皇家空军一次能派出最多的飞机。

输入
第 1 行有 2 个正整数 m 和 n。n 是皇家空军的飞行员总数(n<100);m 是外籍飞行员数(m<=n)。外籍飞行员编号为 1~m;英国飞行员编号为 m+1~n。
接下来每行有 2 个正整数 i 和 j,表示外籍飞行员 i 可以和英国飞行员 j 配合。最后以 2个-1 结束。

输出
第 1 行是最佳飞行员配对方案一次能派出的最多的飞机数 M。接下来 M 行是最佳飞行员配对方案。每行有 2个正整数 i 和 j,表示在最佳飞行员配对方案中,飞行员 i 和飞行员 j 配对。如果所求的最佳飞行员配对方案不存在,则输出‘No Solution!’。

把模板输入输出改一下,再输出Match矩阵就行了,注意,由于要输出匹配,DFS内i要从最大值开始减小。

#include<iostream>
#include<string>
#include<string.h>
//description: this is a program for algorithm-Hungarian
const int MAXN = 101;
using std::string;
//
int n1,n2;//vertexes
int m;
int Matrix[MAXN][MAXN];//Graph
int Visited[MAXN];//sign
int Match[MAXN];//indicate match
//
void print()
{
 for(int i=1;i<=m;i++)
 {
  for(int j=1;j<=m;j++)
  {
   std::cout<<Matrix[i][j]<<" ";
  }
  std::cout<<"\n";
 }
}
int Hungarian(int x)
{
 for(int i=m;i>n2;i--)
 {
  if(!Visited[i]&&Matrix[x][i])//judge unvisited & matchable
  {
   Visited[i]=1;
   if(Match[i]==0||Hungarian(Match[i]))//judge (unmatched) or (have to change match point)
   {
    Match[i]=x;
    return 1;//match successful
   }
  }
 }
 return 0;
}//Hungarian Algorithm
//
int main()
{
 memset(Match,0,sizeof(Match));
 if(std::cin>>n1>>m)
 {
  n2=m-n1;
  memset(Matrix,0,sizeof(Matrix));
  int count=0;
  int row;
  int col;//axis
  while(1)
  {
   std::cin>>row>>col;
   if(row==col&&row==-1)
   {
    break;
   } 
   Matrix[row][col]=1;//connect
  }
  //print();
  for(int i=1;i<=n2;i++)
  {
   memset(Visited,0,sizeof(Visited));
   if(Hungarian(i))
   {
    count++;
   }
   /*
   for(int i=1;i<=m;i++)
   {
    std::cout<
  }
  if(count)
  {
   std::cout<<count<<std::endl;//output
   for(int i=1;i<=m;i++)
   {
    if(Match[i]!=0)
    {
     std::cout<<i<<" "<<Match[i]<<std::endl;
    }
   }
  }
  else
  {
   std::cout<<"No Solution!"<<std::endl;
  }
 }
 return 0;
}

时间有些紧,可能码的代码有些纰漏,之后会更新
U3D项目也会定时更新的

你可能感兴趣的:(数据结构与算法,离散数学)