hdu 2444 The Accomodation of Students(二分图判断+求最大匹配)

    题意:有一群小朋友,其中一部分小朋友互相认识,给出小朋友之间是否认识的关系。首先判断是否能将所有小朋友分成两组,每组中任意两个人都不认识。如果能的话,将两个认识的小朋友分成一组,问最多能分多少组?

    思路:首先是对图进行染色,判断是否能将图分成两个部分,如果不能的话,直接输出“No”,否则继续计算。

    求分组:求二分图最大匹配数。代码中注释的vis[i]=1这个语句开始的时候我一直没有加,当时想的是对于每一个点进行搜索的时候标记,但是这样会出现比如2的子节点为4,然后link[4]==2的循环。注意每一次搜索的是link[son],而不是son。

   对于每次搜索link[son],而不是son,需要特别注意一下。寻找增广路径的过程遵循这样一个规律:对于一条路径,如果它的起始点是未盖点,同时它的最后一个点也是未盖点,那么这条路径就是一条可增广路径。比如说对于1……2——3……4这样的路径,就可以通过修改为1——2……3——4这种方式使配对增加一。而在这个过程中,如果我们检查1的子节点2,这时候如果2是盖点,我们需要的不是对2寻找一个新的配对对象(即使找到了,我们也不能把1和2连接起来,因为2已经在另一个连线中),而是对3寻找一个新的配对对象。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 205
#define M 40005
struct node
{
    int son;
    int next;
} Edge[M*2];
int head[N],vis[N],q[N],link[N];
int n,m,cnt;
void AddEdge(int x,int y)
{
    Edge[cnt].son=y;
    Edge[cnt].next=head[x];
    head[x]=cnt++;
    Edge[cnt].son=x;
    Edge[cnt].next=head[y];
    head[y]=cnt++;
    return ;
}
int bfs(int k)
{
    int h,t;
    h=t=0;
    q[t++]=k;
    vis[k]=0;
    while(h!=t)
    {
        int cur=q[h++];
        int u=head[cur];
        for(int i=u; i!=-1; i=Edge[i].next)
        {
            int son=Edge[i].son;
            if(vis[son]!=-1&&vis[son]!=(vis[cur]+1)%2) return 1;
            else if(vis[son]!=-1) continue;
            vis[son]=(vis[cur]+1)%2;
            q[t++]=son;
        }
    }
    return 0;
}
int dfs(int father)
{
    int u=head[father];
    vis[father]=1;
    for(int i=u; i!=-1; i=Edge[i].next)
    {
        int son=Edge[i].son;
        if(vis[son]) continue;
		vis[son]=1;///开始的时候没有加上这个标记。
        if(link[son]==-1||dfs(link[son])==1)
        {
            link[son]=father;
            return 1;
        }
    }
    return 0;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(head,-1,sizeof(head));
        cnt=0;
        for(int i=1; i<=m; i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            AddEdge(x,y);
        }
        memset(vis,-1,sizeof(vis));
        int flag=0;
        for(int i=1; i<=n; i++)
        {
            if(vis[i]==-1)
            {
                flag=bfs(i);
                if(flag) break;
            }
        }
        if(flag)
        {
            printf("No\n");
            continue;
        }
        memset(link,-1,sizeof(link));
        int count=0;
        for(int i=1; i<=n; i++)
        {
            memset(vis,0,sizeof(vis));
            if(dfs(i)) count++;
        }
        printf("%d\n",count/2);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值