码迷,mamicode.com
首页 > 其他好文 > 详细

二分图【模板】

时间:2015-05-06 00:01:23      阅读:482      评论:0      收藏:0      [点我收藏+]

标签:

二分图:原图G的顶点可以分类两个集合X和Y,所有的边关联的两个顶点恰好一个属于集合X,另一个属于集合Y,则称该图为二分图。
二分图匹配:给定一个二分图G,在G的一个子图M中,M的边集中的任意两条边都不依附于同一个顶点,即一个顶点最多只有一条边。则称M是一个匹配。
二分图最大匹配:图中包含边数最多的匹配称为图的最大匹配。
二分图完美匹配:如果所有点都在匹配边上,则称这个最大匹配是完美匹配。
二分图多重匹配:二分图匹配一对一匹配,这里允许集合Y中的一个元素和集合X中的多个元素匹配(一般有最大限制N),但是集合X中的元素只能和集合Y中的多个元素匹配。
二分图最佳匹配:将二分图加权,在图中找到一个总权值最大的匹配。
二分图最小点覆盖: 最小覆盖要求用最少的点(X集合或Y集合的都行)让每条边都至少和其中一个点关联。
二分图最小点覆盖 = 二分图最大匹配
二分图最小边覆盖:选择最少的边,使得能够覆盖图中所有的点。
二分图最小边覆盖 = 无向图被重复计算两次,ans = N - 最大匹配数/2
DAG图的最小路径覆盖:用尽量少的不相交简单路径覆盖有向无环图
(DAG)G的所有顶点,这就是DAG图的最小路径覆盖问题。
最小路径覆盖 = 节点数N-最大匹配数
二分图的最大独立集:在N个点的图G中选出若干个点,使这若干个点两两之间没有边,求点数最大值。
二分图的最大独立集数 = 节点数(n)— 最大匹配数(m)
一般是双向边,则被重复计算了两次,ans = N - 最大匹配数/2

二分图最大匹配——匈牙利算法DFS版:

const int MAXN = 220;  

bool Map[MAXN][MAXN];  
bool bMask[MAXN];  

int NX,NY;  
int cx[MAXN],cy[MAXN];  

int FindPath(int u)  
{  
    for(int i = 1; i <= NY; ++i)  
    {  
        if(Map[u][i] && !bMask[i])  
        {  
            bMask[i] = 1;  
            if(cy[i] == -1 || FindPath(cy[i]))  
            {  
                cy[i] = u;  
                cx[u] = i;  
                return 1;  
            }  
        }  
    }  
    return 0;  
}  

int MaxMatch()  
{  
    int res = 0;  
    for(int i = 1; i <= NX; ++i)  
        cx[i] = -1;  
    for(int i = 1; i <= NY; ++i)  
        cy[i] = -1;  

    for(int i = 1; i <= NX; ++i)  
    {  
        if(cx[i] == -1)  
        {  
            for(int j = 1; j <= NY; ++j)  
                bMask[j] = 0;  
            res += FindPath(i);  
        }  
    }  
    return res;  //返回最大匹配数
}  

二分图多重匹配:

const int MAXN = 33;    //最大顶点数

int Map[MAXN][MAXN];    //二分图
bool Mask[MAXN];        //寻找增广路径时的标志数组
int NX,NY,N;            //NX左集合顶点数,NY右集合顶点数

int vcy[MAXN];          //vcy[i]表示右集合i顶点匹配到左集合的顶点数目
int cy[MAXN][MAXN];     //cy[i][j]表示与右集合i顶点匹配的第j个元素

int limit[MAXN];        //每个右集合各顶点最多匹配左集合顶点的个数
//也可以为limit,表示最多匹配左集合顶点的共同限制数,如果为待求元素,可二分搜索查找答案
bool FindPath(int u)    //寻找增广路径
{
    for(int i = 1; i <= 5; ++i)
    {
        if(Map[u][i] && !Mask[i])
        {
            Mask[i] = 1;
            if(vcy[i] < limit[i])   //vcy[i] < limit
            {
                cy[i][vcy[i]++] = u;
                return true;
            }

            for(int j = 0; j < vcy[i]; ++j)   //j < vcy[i]
            {
                if(FindPath(cy[i][j]))
                {
                    cy[i][j] = u;
                    return true;
                }
            }
        }
    }
    return false;
}

void MulMatch()     //求多重匹配
{
    int Ans = 0;
    memset(vcy,0,sizeof(vcy));
    for(int i = 1; i <= N; ++i)
    {
        memset(Mask,0,sizeof(Mask));
        Ans += FindPath(i); //计算右边能匹配点个数
        /*
        if(!FindPath(i))
            return false;
        */

    }
    //return true;
    if(Ans == N)
        printf("T-shirts rock!\n");
    else
        printf("I‘d rather not wear a shirt anyway...\n");
}

二分图最佳匹配——KM算法:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 330;
const int INF = 0xffffff0;

int N,NX,NY;
int link[MAXN],lx[MAXN],ly[MAXN],slack[MAXN];
int visx[MAXN],visy[MAXN];  //标记
int Map[MAXN][MAXN];    //存放权值
//lx[],ly[]顶标; link[]记录匹配值
int FindPath(int u) //回溯寻找最优解
{
    visx[u] = 1;
    for(int i = 1; i <= NY; ++i)
    {
        if(visy[i])
            continue;
        int temp = lx[u] + ly[i] - Map[u][i];
        if(temp == 0)
        //if(Map[u][i] == lx[u] + ly[i])    //说明是相等子图
        {
            visy[i] = 1;
            if(link[i] == -1 || FindPath(link[i]))
            {
                link[i] = u;
                return 1;
            }
        }
        else if(slack[i] > temp)
            slack[i] = temp;
    }
    return 0;
}

int KM()    //求权值最大的最佳匹配
{
    memset(ly,0,sizeof(ly));
    memset(link,-1,sizeof(link));
    for(int i = 1; i <= NX; ++i)
    {
        lx[i] = -INF;   //求最小权匹配则 lx[i] = INF
        for(int j = 1; j <= NY; ++j)
            if(Map[i][j] > lx[i])   //最小权匹配则更改符号
                lx[i] = Map[i][j];
    }

    for(int i = 1; i <= NX; ++i)
    {
        for(int j = 1; j <= NY; ++j)
            slack[j] = INF;
        while(1)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(FindPath(i))
                break;
            int d = INF;
            for(int j = 1; j <= NY; ++j)
                if(!visy[j] && d > slack[j])
                    d = slack[j];
            if(d == INF)
                return ;
            for(int j = 1; j <= NX; ++j)
                if(visx[j])
                    lx[j] -= d;     //求最小权值则改为 lx[j] += d;
            for(int j = 1; j <= NY; ++j)
                if(visy[j])
                    ly[j] += d;     //求最小权值则改为 ly[j] -= d;
                else
                    slack[j] -= d;
        }
    }
    int res = 0;
    for(int i = 1; i <= NY; ++i)
        if(link[i] > -1)
            res += Map[link[i]][i];
    return res; //输出最佳匹配的最大权值和
}


int main()
{
    int N;
    while(~scanf("%d",&N))
    {
        NX = NY = N;
        for(int i = 1; i <= N; ++i)
            for(int j = 1; j <= N; ++j)
                scanf("%d",&Map[i][j]);

        printf("%d\n",KM());
    }

    return 0;
}

二分图【模板】

标签:

原文地址:http://blog.csdn.net/lianai911/article/details/45506473

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!