이분 그래프 일치-헝가리 알고리즘

매칭이란

일치 : 그래프 이론에서 "일치"는 두 가장자리에 공통 꼭지점이없는 가장자리 집합입니다.
최대 일치 : 그래프의 모든 일치 항목 중에서 가장 일치하는 모서리가 일치하는 것을이 그래프의 최대 일치라고합니다.

이분 그래프의 일치 : 이분 그래프 G가 주어지면 G의 하위 그래프 M에서 M의 가장자리 집합 {E}에있는 두 가장자리가 동일한 정점에 연결되지 않은 경우 M이 일치라고합니다.
이분 그래프의 최대 일치 : 모든 일치 항목 중 가장 많은 가장자리를 포함하는 일치 그룹을 이분 그래프 의 최대 일치라고하며 가장자리 수는 최대 일치 수입니다.

헝가리 알고리즘

이전의 채색 방법 은 그래프가 이분 그래프인지 여부를 판단하는 것이고,
헝가리 알고리즘 은 이분 그래프를 기반으로 이분 그래프의 최대 일치를 찾는 알고리즘 입니다.

이분 그래프는 정점을 두 세트 A와 B로 나눕니다. 세트 A와 B 사이에는 간선이 없으며 A와 B의 점 사이에는 간선이 있습니다. (A의 점 x와 B의 일부 점 사이에 모서리가 있습니다. 하나가있을 수도 있고 많을 수도 있고 없을 수도 있습니다.)
최대 일치는 가능한 한 많은 모서리를 선택하는 것입니다. A와 B Matching의 점, 일대일 일치, 즉 일치하는 항목은 다시 일치 될 수 없으므로 두 모서리에 공통 정점이 없습니다.

이분 그래프는 일반적으로 무 방향 그래프입니다., 양방향 일치, 일치하려고 할 때 한 방향에서 다른 방향으로 만 일치하면됩니다. 다음 질문과 코드는 집합 A에서 시작하여 집합 B의 중간까지 일치한다고 가정합니다 . 따라서 무 방향 그래프 이지만 A-> B 의 간선 만 저장할있지만 B-> A의 간선은 저장할 수 없으므로 정점을 자동으로 두 세트로 나눌 수 있으며 노드의 수는 두 세트 모두 1에서 시작할 수 있습니다 .

매칭 과정

이미 찾고있는 여자는 남자 친구가있는 경우,
당신은 그녀의 남자 친구를 요청할 수 있습니다
당신은 스페어 타이어가있는 경우.
당신이 나에게 줄 수 있습니까?
그가 놓을 수
있다면 놓아주지 못한다면 그들은 진정한 사랑입니다.
다음 스페어 타이어를 찾습니다.

이때 임시 매칭 관계 a1 ---- b1, a2 ---- b2가 있다고 가정하면 A의 정점 a1에는 b1, b2, a2를 가리키는 점이 있고 b2를 가리키는 가장자리 만 있고 a3은 b1, b3 등을 가리 킵니다. ……의 가장자리에 대해 A에서 정점 a3의 일치를 예로 들어 보겠습니다.

일시적으로 일치하는 관계 a1 ---- b1, a2 ---- b2가 있다고 가정하면 a2는 b1을 가리키는 가장자리를 가질 수 없습니다. 그렇지 않으면 헝가리 알고리즘 코어 1을 따르지 않습니다.

다음 분석 프로세스 :

a3에 대해 일치하는 개체 를 찾습니다. A3은 먼저 b1을 찾고 b1이 일치하는 것을 찾습니다. 그러나 이때 a3은 b3을 찾지 않고 b1에게 일치하는 개체 a1을 찾도록 요청하고 a1이 일치 할 다른 점을 찾을 수 있는지 확인합니다. , 이때 a1에 대해 일치하는 객체를 찾는 문제로 돌아가 지만 일치하는 객체 b1은 금지 되어 있으므로 a1은 더 이상 b1을 찾을 수 없으며 b2로 이동하면 b2는 금지되지 않고 찾을 수 있지만 b2 이미 일치하는 객체 A2를 가지고 있으며, A2가 일치하는 개체를 변경할 수 있다면 부탁드립니다.이 때, 그것은 다시 A2에 대한 일치하는 객체를 찾는 문제이다 . 동시에, 점 B1 및 B2는 금지 , 점 때문에 b1은 a3이 원하는 지점이고 b2 Point는 a1이 원하는 지점이며 모두 보류 중입니다 .하지만 A2는 돌아갈 수있는 여지가 없습니다., 이때 a2와 b2는 진정한 사랑이며 영구적으로 일치하고 잠겨 있습니다. 그런 다음 돌아가서 a1, b2는 좋지 않다고 말하면 다음을 찾을 수 있지만 a1도 후퇴 할 여지가 없으므로 a1과 b1도 진정한 사랑이며 영구적으로 일치하고 잠겨 있습니다. 그런 다음 a3, b1이 좋지 않다고 말하면 다음을 찾을 수 있습니다. 그러면 a3이 b3을 찾기 시작합니다 ...

상기 일치하는 배열과 일치 할 수 금지 상태가 배열대로 힘 나타낼 수 잠겨 성 배열을 나타낼 수있다
match[j]=i일시적으로 매칭되는 세트 및 세트 B의 점 I j를 나타내는 점
st[j]=true의 정점 j를 일시적으로 비활성화
vis[j]=true나타내는 match[i]=jI 및 j는 진정한 사랑입니다. 나중에 오는 정점에 대해 정점 j에 대해 생각하지 마십시오.

a1에 b1과 b2를 가리키는 모서리 만 있고 a2에 b1, b2를 가리키는 모서리 만 있고 a3에 b1, b2 및 b3을 가리키는 모서리가 있다고 가정합니다.
먼저 a1을 선택하고 일치 [a1] = b1
을 선택한 다음 선택합니다. a2, b1이 점유 된 것을 확인합니다. 그런 다음 a1이 양보하도록합니다. 이때 match [a1] = b2; match [a2] = b1;
그런 다음 a3이를 선택 하고 b1이 점유 된 다음 a3이 a2를 양보하도록합니다. . A2는 b1을 선택할 수 없지만 b2, a2 ​​만 선택할 수 있습니다. a1이 양보하도록 강요합니다. 이때 a1은 b1과 b2를 선택할 수 없습니다. A1은 후퇴 할 여지가 없습니다. A1과 b2는 잠겨 있습니다. a2에게 메시지를 말한 후 , a2 후퇴 할 공간이없고 a2와 b1이 잠겨 있습니다.
a3가 b2를 찾으면 a3에게 직접 말하고 b2가 잠겨 있고 표시되지 않으면 다음 항목을 찾을 수 있습니다.
다음으로, a3은 b3을 찾고 match [a3] = b3;

또 다른 예는 헝가리 알고리즘의 핵심 을 설명 하는 것입니다.

  1. 첫 번째 일치는 일시적이며 두 번째 일치는 첫 번째 일치가 아닌 한 첫 번째 일치가 가능한 한 멀리 일치해야하기 때문에 얻을 가능성이 가장 높습니다.수율후퇴가없는 지점까지.
  2. 이것은 재귀 적 프로세스입니다. 재귀 의 끝에서 일치하는 것은이 일치의 정점이 1 차이고 한 가장자리 만 다른 집합으로 확장되기 때문일뿐만 아니라 하나의 경우에 불과합니다. 일반적인 상황은 다음과 같습니다. 나는 후퇴 할 수없는 지점에 이르렀고, 더 이상 다른 정점에 줄 수 없으며, 나 자신을 놔 두었습니다.

제목 설명

이분 그래프가 주어지면 집합의 왼쪽 절반에는 n1 개의 점 ( 1-n1 )이 포함되고, 오른쪽 절반에는 n2 개의 점 ( 1-n2 )이 포함되며, 이분 그래프에는 총 m 개의 간선이 포함됩니다.

데이터는 모서리의 두 끝 점이 같은 부분에있을 수 없음을 보장합니다.

이분 그래프의 최대 일치 수를 찾으십시오.

입력 형식 첫
번째 줄에는 세 개의 정수 n1, n2 및 m이 포함됩니다.

다음 m 개 행에서 각 행에는 두 개의 정수 u와 v가 포함되어 있으며, 이는 점 집합의 왼쪽 절반에있는 점 u와 점 집합의 오른쪽 절반에있는 점 v 사이에 가장자리가 있음을 나타냅니다.

출력 형식
이분 그래프의 최대 일치 수를 나타내는 정수를 출력합니다.

데이터 범위
1≤n1,
n2≤500,
1≤u≤n1,
1≤v≤n2 , 1≤m≤10 5

입력 샘플 :

222 4
1 1
2
2 1
2 2

샘플 출력 :

2

알고리즘 구현

#include <iostream>
#include <cstring>

using namespace std;

#define read(x) scanf("%d",&x)
const int N=510,M=1e5+10;  //虽是无向图,但是存储单方向的边A->B即可,到时候从A找点匹配B
int h[N],e[M],ne[M],idx;
int match[N]; 
bool vis[N],st[N];
//三个数组维护的含义见上面或下面
int n1,n2,m;
"n1+n2是总顶点数,n1是A集合中顶点数,n2是B集合中顶点数,由题目要求限制"

void add(int a,int b)
{
    
    
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

int find(int v)
{
    
    
    for (int i=h[v];~i;i=ne[i]) {
    
    
        int j=e[i];
        if (!vis[j] && !st[j]) {
    
    
            if (match[j]==0) {
    
    match[j]=v; return true;}
            else {
    
    
                st[j]=true;
                if (find(match[j])) {
    
    match[j]=v; return true;}
                else vis[j]=true;
            }//此时说明B堆中的j结点与A堆中的结点match[j]是板上钉钉的匹配了,不能再改了,
        }    //所以后面的点再匹配就不用再找我了,
    }
    return false;
}

int main()
{
    
    
    memset(h,-1,sizeof h);
    read(n1),read(n2),read(m);//A堆顶点数n1,B堆顶点数n2,边数m
    int a,b;
    while (m--) {
    
    
        read(a),read(b);
        add(a,b);"两个堆中结点编号重复,这里无影响,也可以add(a,n1+b);"
    }
    int res=0; //统计最大匹配的边数
    for (int i=1;i<=n1;i++) {
    
    
        memset(st,false,sizeof st); //每次初始化一下st数组,重新禁止一些匹配。
        if(find(i)) res++; 
    }
    printf("%d",res);
    
    return 0;
}

일치 배열 :
A 힙을 가로 질러 A-> B의 가장자리를 저장합니다. 이때 match [i] = j;는 B 파일의 꼭지점 i가 A 파일의 꼭지점 j와 일치 함을 의미합니다. 그래프에서 꼭지점의 첨자는 모두 1부터 시작하므로 match [i] = 0 일 때 , 아직 일치하지 않음을 의미합니다.

st 배열 : st 배열
의 기능은 왼쪽 지점이 이미 오른쪽에 객체가있는 지점과 일치 할 때 오른쪽 지점이 측면의 왼쪽 지점에 해당하여 새로운 "객체"를 찾고 더 이상 찾을 수 없다는 것입니다. 다중 재귀로 인해 원본의 오른쪽에있는 지점은 각 재귀 이전에 금지 된 지점을 기록해야하므로 st 배열을 만들고 새 일치가 시작될 때마다 st 배열을 false로 재설정합니다.

vis 배열 :
vis 배열은 B 힙에서 선택되지 않은 포인트를 기록하는 데 사용됩니다. B 힙의 포인트가 A 힙의 포인트와 만 일치하면 잠기므로 찾을 수 없습니다. 일치 []).

기본 버전에서는 st 배열의 함수를 대체하기 위해 if (! vis [j] && match [j]! = x)에서 match [j]! = x를 사용하고 싶지만 각 재귀가 aj를 금지하기 때문에, 그리고 이것은 현재 j가 금지되고 이전 j에는 영향을 미치지 않으므로 쉽게 무한 루프가 발생합니다. 버스트 스택.

 bool find(int x)
{
    
    
    for (int i = h[x]; i != -1; i = ne[i])  {
    
    
        int j=e[i];
        if (!vis[j] && match[j]!=x) {
    
    
            if (match[j]==0) {
    
    match[j]=x; return true;}
            else {
    
    
                if (find(match[j])) {
    
    match[j]=x; return true;}
                else vis[j] = true;
            }                     
        }
    }
    return false;
}

5 행의 판단이 다르지만 잘못된 것입니다.

추천

출처blog.csdn.net/HangHug_L/article/details/114106714