题目链接:https://vjudge.net/problem/18341/origin
题目大意:给你n个学生,m个朋友关系。问你能不能把学生分成二组,每一组的人没有互相认识。如果可以,这样对认识的人分配双人房。问你最多可以分配双人房。

思路:判断是不是二分图,如果是最大匹配/2就可以了。

#include<bits/stdc++.h>
using namespace std;
#define LL long long

int g[205][205]={0};//存图
int vist[205]={0};//染每种颜色的节点
int n;


int isTwo()//判断是否为二分图
{
    queue<int>q;
    memset(vist,0,sizeof(vist));
    q.push(1); vist[1]=1;
    while(!q.empty())
    {
        int p=q.front(); q.pop();
        for(int j=1;j<=n;j++)
        if(g[p][j])
        {
            if(vist[j]==0)
            {
                if(vist[p]==1)vist[j]=2;else vist[j]=1;
                q.push(j);
            }
            else if(vist[j]==vist[p])
                return 0;
        }
    }
    return 1;
}

const int N=205;  // 最大点数
const int INF=1<<28;  // 距离初始值

int Mx[N];  //Mx[i]表示左集合i顶点所匹配的右集合的顶点序号
int My[N];  //My[i]表示右集合i顶点所匹配的左集合的顶点序号
int dx[N];
int dy[N];
bool used[N];

int Nx,Ny,dis;


 //寻找 增广路径集
bool searchP()
{
    dis=INF;
    int i,v,u;
    std::queue<int> Q;

    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(i=1;i<=Nx;i++)
    {   //cx[i]表示左集合i顶点所匹配的右集合的顶点序号
        if(Mx[i]==-1)
        {
            //将未遍历的节点 入队 并初始化次节点距离为0
            Q.push(i);
            dx[i]=0;
        }
    }
    //广度搜索增广路径
    while(!Q.empty())
    {
        u=Q.front();
        Q.pop();
        if(dx[u]>dis) break;
        //取右侧节点
        for(v=1;v<=Ny;v++)
        {
            //右侧节点的增广路径的距离
            if(g[u][v]&&dy[v]==-1)
            {
                dy[v]=dx[u]+1;   //v对应的距离 为u对应距离加1
                if(My[v]==-1) dis=dy[v]; //如果该点未被匹配,那么增广路形成
                else                       //如果该点匹配了,那么接着往下搜
                {
                    dx[My[v]]=dy[v]+1;
                    Q.push(My[v]);
                }
            }
        }
    }
    return dis!=INF;
}

//寻找路径 深度搜索
bool DFS(int u)
{
    int v;
    for(v=1;v<=Ny;v++)
    {
        //如果该点没有被遍历过 并且距离为上一节点+1
        if(g[u][v]&&!used[v]&&dy[v]==dx[u]+1)
        {
            //对该点染色
            used[v]=true;
            if(My[v]!=-1&&dy[v]==dis) continue; //如果该点已经被匹配了并且为最后一个匹配点,那么这条路径不是增广路。即是说这条路的结点已经匹配
            if(My[v]==-1||DFS(My[v]))  //如果该点未匹配或者后面的点能腾出位置,那么这就是增广路
            {
                My[v]=u;
                Mx[u]=v;
                return true;
            }
        }
    }
    return false;
}

//得到最大匹配的数目
int Hungary()
{
    int u;
    int ret=0;
    memset(Mx,-1,sizeof(Mx));
    memset(My,-1,sizeof(My));
    while(searchP())  //如果存在增广路
    {
        memset(used,false,sizeof(used));
        for(u=1;u<=Nx;u++)
           if(Mx[u]==-1&&DFS(u))  ret++;
    }
    return ret;
}

int main()
{
    int k;
	while(~scanf("%d%d",&n,&k))
    {
        memset(g, 0, sizeof(g));
        for(int i=0;i<k;i++)
        {
            int u, v;
            scanf("%d%d",&u,&v);
            g[u][v]=1;
            g[v][u]=1;
        }
        Ny=Nx=n;

        if(isTwo())
        {
            int ans=Hungary();
            printf("%d\n",ans/2);
        }
        else
        {
            printf("No\n");
        }
    }

	return 0;
}