【二分图染色+匈牙利算法最大匹配】 HDU - 2444 Q - The Accomodation of Students

Q - The Accomodation of Students  HDU - 2444 

There are a group of students. Some of them may know each other, while others don't. For example, A and B know each other, B and C know each other. But this may not imply that A and C know each other.

Now you are given all pairs of students who know each other. Your task is to divide the students into two groups so that any two students in the same group don't know each other.If this goal can be achieved, then arrange them into double rooms. Remember, only paris appearing in the previous given set can live in the same room, which means only known students can live in the same room. 

Calculate the maximum number of pairs that can be arranged into these double rooms. 

Input

For each data set: 
The first line gives two integers, n and m(1<n<=200), indicating there are n students and m pairs of students who know each other. The next m lines give such pairs. 

Proceed to the end of file. 
 

Output

If these students cannot be divided into two groups, print "No". Otherwise, print the maximum number of pairs that can be arranged in those rooms. 

Sample Input

4 4
1 2
1 3
1 4
2 3
6 5
1 2
1 3
1 4
2 5
3 6

Sample Output

No
3

       建立一张二分图,左右两边都是图的所有节点,取左端节点。(题目中可能已直接给出二分图;也可能题目中只给出所有相连点对构成的图,可以用用链表存储边到边的关系,如下面的示例代码,如果每条边按两个方向分别存储一次那么遍历每一个点求出的最大匹配数是两个完全相同的二分图的匹配数之和,/2即可;还有其他存储方式,比如vector的邻接表,或二维数组的邻接矩阵都可以)

      判断与该左端结点B相连的右端节点C是否已是匹配点(是否已加入匹配),如果未加入则加入匹配,即做标记link[该右端节点]=左端结点,返回TRUE并使当前最大匹配数+1;或者如果该右端节点C已经存在另一个左端的节点A与之相连(该右端节点已是匹配点),则判断该该右端节点本来与之相连的左端节点是否在右端还有另外的节点可以与之相连,如果右端有相连的未匹配点D则将D加入匹配或者如果有相连的已匹配点则继续递归,如果找到符合要求的点则对这一路上更改过的点的link标记做更改,最后返回TRUE并使当前最大匹配数+1。

       继续遍历,直到左端所有的点都已遍历一遍或当前最大匹配数已经达到最大(已是完美匹配)则结束。

先是二分图染色判断是不是二分图
再用匈牙利算法找二分图最大匹配

#include <bits/stdc++.h>
using namespace std;
const int maxn=405;
int vis[maxn],col[maxn],linker[maxn];
int n,m,k;
vector <int> G[maxn];

void init()
{
    for(int i=1;i<=n;i++) G[i].clear();
    memset(linker,-1,sizeof(linker));
    memset(col,-1,sizeof(col));
}

int bfs(int x)
{
    queue <int> q;
    q.push(x);
    col[x]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            if(col[v]==-1) //如果没有被染过色
            {
                col[v]=!col[u]; //就让它染成和相邻点为不同色,最后只会是0或1
                q.push(v);
            }
            else
            {
                if(col[v]==col[u])  //如果染过色了,而且相邻点颜色相同,代表二分图无法形成,不是所有路径都二分,输出No
                    return 0;
            }
        }
    }
    return 1;
}

int dfs(int u)
{
    for(int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(!vis[v])  //如果没有被找过
        {
            vis[v]=1;  //标记已找过
            if(linker[v]==-1||dfs(linker[v]))  //这里寻找增广路径的结束条件就是找到Y中未被配对过的点
            {                                  //如果这点被配对过,那么就从这点配对的X中的点进行新一轮的dfs()
                linker[v]=u;
                return 1;
            }
        }
    }
    return 0;
}

int maxmatch()
{
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof(vis));
        if(dfs(i)) ans++;  //如果可以找到增广路就加1
    }
    return ans/2;  //因为是无向图所以要/2,不是每题都这样,视情况而定
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        k=0;
        init();
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
        }

        for(int i=1;i<=n;i++)
        {
            if(col[i]==-1&&!bfs(i))
            {
                cout<<"No"<<endl;
                k=1; break;
            }
        }
        if(k==1) continue;
        else cout<<maxmatch()<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41037114/article/details/81539283