hdu 2444 二分图判断与最大匹配(模板修改好题+二分图的判断+二分图最大匹)

题意:有n个学生,有m对人是认识的,每一对认识的人能分到一间房,问能否把n个学生分成两部分,每部分内的学生互不认识,而两部分之间的学生认识。如果可以分成两部分,就算出房间最多需要多少间,否则就输出No。

首先判断是否为二分图,然后判断最大匹配

思路: 无向图G为二分图的充分必要条件是,G至少有两个顶点,且其所有回路的长度均为偶数。回路就是环路,也就是判断是否存在奇数环。

①用染色法判断是否是二分图:

   主要是怎么判断一个图是不是二分图。不妨选取某个点作为起点并染为某种颜色、同时把与它相邻的元素染为对立的颜色,进行BFS(DFS也可以),如果到某一步发现当前点和相邻点的颜色一样,那么就出现了矛盾,就不是二分图。

②匈牙利算法求最大匹配数

原理 是通过DFS或者BFS进行遍历,遇到新的未匹配的点或者有增广路(若有增广路就可以反转,则最大匹配数就可以+1)的出现,便返回TRUE使当前最大匹配数+1,然后继续遍历,直到完成遍历或者已找出最大匹配数。


接下来会面临一个比较有趣的问题,我们确定现在的图是二分图,然后我们要求它的最大匹配——这里涉及到一个很关键的问题,就是一个图我们说他自己是一个二分图,那么是他内部的一些点会分成两部分,分别写成两列变成了形式上的二分图。而我们求二分图的时候是分别写成两列的话是一个图的所有点,因此总数最后是要除以2的,但是还有一种写法,可以看看。

代码:#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <cstdio>
#include <set>
#include <cmath>
//#include <map>
#include <algorithm>
#define INF 0x3f3f3f3f
#define MAXN 205
#define Mod 10001
using namespace std;
bool vis[MAXN];
int n,m;
int mp[MAXN][MAXN],pre[MAXN];  //匹配路径;
int find(int cur)
{
   
for(int i=1; i<=n; ++i)
    {
       
if(!vis[i]&&mp[cur][i])
        {
            vis[i] =
true;
           
if(pre[i] == 0 || find(pre[i]))
            {
                pre[i]=cur;
               
return 1;
            }
        }
    }
   
return 0;
}
int judge[MAXN];
bool bfs()
{
   
memset(judge,-1,sizeof(judge));
   
queue<int> q;
    q.push(
1);
    judge[
1]=0;
   
while(!q.empty())
    {
       
int v=q.front();
        q.pop();
       
for(int i=1;i<=n;++i)
        {
           
if(mp[v][i])
            {
               
if(judge[i]==-1)
                {
                    judge[i]=(judge[v]+
1)%2;
                    q.push(i);
                }
               
else
                {
                   
if(judge[i]==judge[v])
                       
return false;
                }
            }
        }
    }
   
return true;
}
int main()
{
   
while(~scanf("%d%d",&n,&m))
    {
       
memset(mp,0,sizeof(mp));
       
memset(pre,0,sizeof(pre));
       
int a,b;
       
for(int i=0;i<m;++i)
        {
           
scanf("%d%d",&a,&b);
            mp[a][b]=
1;
            mp[b][a]=
1;//注意双向存图
        }
       
if(!bfs())
        {
           
puts("No");
           
continue;
        }
       
int ans=0;
       
for(int i=1;i<=n;++i)
        {
           
memset(vis,0,sizeof(vis));
           
if(find(i))
                ans++;
        }
       
printf("%d\n",ans/2);//注意/2
    }
   
return 0;
}




扫描二维码关注公众号,回复: 1269765 查看本文章


代码:
#include<cstdio>
#include<string>
#include<string.h>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<cmath>
#include<vector>
#include<set>
#include<queue>
using namespace std ;
int n,m;
const int maxn= 222 ;
int G[maxn][maxn];
int linker[maxn];
int used[maxn];
int color[maxn];
int vis[maxn];

int dfs(int u) {
for ( int v= 1 ;v<=n;v++){
if (G[u][v]&&!used[v]){
used[v]=
1 ;
if (linker[v]== -1 ||dfs(linker[v])){
linker[v]=u;//注意linker有值表示已经有主,匹配过了,便于下方的跳过操作
linker[u]=v;
return 1 ;
}
}
}
return 0 ;
}

int hungry(void) {
int res= 0 ;
memset (linker, -1 , sizeof (linker));
for ( int u= 1 ;u<=n;u++){
if (linker[u]!= -1 ) continue ;//与上面函数对应,如果已经匹配过了,就说明这个点已经被分好了
memset (used, 0 , sizeof (used));
if (dfs(u)){
res++;
}
}
return res;
}
int flag;
void judge(void) {
memset (color, -1 , sizeof (color));
queue < int >q;
q.push (
1 );
color[
1 ]= 0 ;
while (q.size ()){
int temp=q.front ();
q.pop();
for ( int i= 0 ;i<=n;i++){
if (G[temp][i]){
if (color[i]== -1 ){
color[i]=(color[temp]+
1 )% 2 ;
q.push (i);
}
else {
if (color[i]==color[temp]){
flag=
0 ;
return ;
}
}
}
}
}
return ;
}

int main() {
while (~ scanf ( "%d%d" ,&n,&m)){
memset (color, -1 , sizeof (color));
memset (vis, 0 , sizeof (vis));
memset (G, 0 , sizeof (G));
flag=
1 ;
while (m--){
int x,y;
scanf ( "%d%d" ,&x,&y);
G[y][x]=G[x][y]=
1 ;//注意双向
}
judge();

if (flag== 0 ) printf ( "No\n" );
else printf ( "%d\n" ,hungry());//注意此解法不用/2
}
}

猜你喜欢

转载自blog.csdn.net/running_acmer/article/details/80431646