二分图的染色与匹配

二分图:图中点集可分成两部分,使处于同一部分的点间没有连边
染色法,是一种简单地判断是否为二分图的方法.
对图中的每个点进行染色,使相临的点颜色不同,如果可以完成则为二分图,否则不为.

BFS实现:

#include
#include
#include
#include
#include
#include
using namespace std;
int n,m,ru,rv,tot;
int first[100010],nxt[100010],color[100010];
bool flag;
struct edge
{
    int u,v;
}l[100010];
queue<int>q;
void build(int f,int t)
{
    l[++tot]=(edge){f,t};
    nxt[tot]=first[f];
    first[f]=tot;
}
int bfs(int s)
{
    q.push(s);
    color[s]=1;
    while(!q.empty())
    {
        int k=q.front();
        q.pop();
        for(int i=first[k];i!=-1;i=nxt[i])
        {
            int x=l[i].v;
            if(color[x]==-1)
            {
                q.push(x);
                color[x]=color[k]^1;//color[x]=!color[k];
            }
            if(color[x]==color[k])
            return 0;
        }
    }
    return 1;
}
int main()
{
    memset(first,-1,sizeof(first));
    memset(color,-1,sizeof(color));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&ru,&rv);
        build(ru,rv);
        build(rv,ru);
    }
    for(int i=1;i<=n;i++)//遍历每一个点 
    {
        if(color[i]==-1&&first[i]!=-1)//若起始点没有染过色(此点及此点可到达的点未被染色)并且起始点有出边 
        {
            if(!bfs(i))
            flag=1;
        }
    }
    if(flag==1)
    printf("NO");
    else printf("YES");
    return 0;
}


DFS实现:
http://acm.hdu.edu.cn/showproblem.php?pid=5285 wyh2000 and pupil
poor wyh…

#include
#include
#include
#include
#include
using namespace std;
int n,m,ru,rv,tot,zero,one,t,ans1,ans2;
int first[200010],nxt[200010],color[200010];
bool flag;
struct edge
{
    int u,v;
}l[200010];
void build(int f,int t)
{
    l[++tot]=(edge){f,t};
    nxt[tot]=first[f];
    first[f]=tot;
}
void dfs(int s)
{
    for(int i=first[s];i!=-1;i=nxt[i])
    {
        int x=l[i].v;
        if(color[x]==color[s])
        flag=1;
        if(color[x]!=-1)
        continue;
        if(color[x]==-1)
        {
            color[x]=color[s]^1;
            if(color[x]==1)
            one++;
            else zero++;
        }
        dfs(x);
    }
}
int main()
{
    scanf("%d",&t);
    for(int i=1;i<=t;i++)
    {
        tot=0;
        memset(first,-1,sizeof(first));
        memset(color,-1,sizeof(color));
        scanf("%d%d",&n,&m);
        if(n<=1)
        {    
            printf("Poor wyh\n");    
            continue;    
        }    
        if(m==0)
        {    
            printf("%d 1\n",n-1);    
            continue;    
        }   
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&ru,&rv);
            build(ru,rv);
            build(rv,ru);
        }
        ans1=ans2=flag=0;
        for(int i=1;i<=n;i++)
        {
            if(color[i]==-1&&first[i]!=-1)
            {
                one=1;
                zero=0;
                color[i]=1;
                dfs(i);
                ans1+=max(one,zero);
                ans2+=min(one,zero);
            }
            if(color[i]==-1&&first[i]==-1)
            ans1++; 
        }
        if(flag==1)
        printf("Poor wyh\n");
        else printf("%d %d\n",ans1,ans2);
    }
    return 0;
}


二分图匹配算法:匈牙利算法
https://www.luogu.org/problem/show?pid=3386 二分图匹配模板

#include
#include
#include
#include
#include
using namespace std;
int n,m,e,ru,rv,ans,tot;
int x[100010],y[100010],first[500010],nxt[500010];
bool vis[100010];
struct edge
{
    int u,v;
}l[500010];
void build(int f,int t)
{
    l[++tot]=(edge){f,t};
    nxt[tot]=first[f];
    first[f]=tot;
}
int path(int k)
{
    for(int i=first[k];i;i=nxt[i])
    {
        int t=l[i].v;
        if(!vis[t])
        {
            vis[t]=1;
            if(!y[t]||path(y[t]))//若t没有匹配或与t匹配的点y[t]可以寻找一条增广路与其他的点进行匹配 
            {
                x[k]=t;//匹配 
                y[t]=k;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    scanf("%d%d%d",&n,&m,&e);
    for(int i=1;i<=e;i++)
    {
        scanf("%d%d",&ru,&rv);
        if(ru>n||rv>m)//洛谷数据有误...
        continue;
        build(ru,rv);
    }
    for(int i=1;i<=n;i++)
    {
        if(!x[i])
        {
            memset(vis,0,sizeof(vis));
            ans+=path(i);//统计匹配数 
        }
    }
    printf("%d",ans);
    return 0;
}
二分图的最小顶点覆盖数=二分图的最大匹配数
二分图的最小路径覆盖数=点的数量-二分图的最大匹配数
二分图的最小边覆盖数=点的数量-二分图的最大匹配数
二分图的最大独立集大小=点的数量-二分图的最大匹配数


推荐博客:http://www.cnblogs.com/shenben/p/5573788.html 对匈牙利算法进行详尽的理解
推荐题目:http://codevs.cn/problem/1222/ 信与信封问题
先进行一次匹配,再对匹配的边依次删除,如果不能完美匹配,说明这条边是不可或缺的,则将这条边输出

#include
#include
#include
#include
#include
using namespace std;
int n,x,y,ans;
int lkx[100010],lky[100010],mp[2500][2500];
bool flag;
bool vis[100010];
int path(int k)
{
    for(int i=1;i<=n;i++)
    {
        if(!mp[k][i]&&!vis[i])
        {
            vis[i]=1;
            if(!lky[i]||path(lky[i]))
            {
                lkx[k]=i;
                lky[i]=k;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    scanf("%d",&n);
    while(1)
    {
        scanf("%d%d",&x,&y);
        if(x==0&&y==0)
        break;
        mp[x][y]=1;
    }
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof(vis));
        if(path(i))
        ans++;
    }
    if(ans!=n)
    printf("none");
    else
    {
        for(int i=1;i<=n;i++)
        {
            memset(vis,0,sizeof(vis));
            int tmp=lkx[i];
            mp[i][tmp]=1;
            lkx[i]=0;
            lky[tmp]=0;
            if(!path(i))
            {
                printf("%d %d\n",i,tmp);
                lkx[i]=tmp;
                lky[tmp]=i;
                flag=1;         
            }
            mp[i][tmp]=0;
        }
        if(!flag)
        printf("none");
    }
    return 0;
}

你可能感兴趣的:(>算法集合(_▽_)<,二分图染色,匈牙利,NOIPRP++)