HDU2444 二分图判断(BFS 的染色法) + 求最大匹配边数(DFS 的匈牙利算法)

0)二分图匹配有两种算法,一种是网络流中的最大流,另一种就是下面的匈牙利算法。


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

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


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


I原理:

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


II情景理解:

    在这个用于理解的示例中,为了描述方便,采用line[i][j]二维数组描述boy[i]与girl[j]之间是否互相喜欢,boy最多有n个,girl最多有m个,现在求怎样搭配他们,让他们成为情侣的对数最多。

【下面的例子中,是男生主动向女生告白、挑选女生,换成女生主动向男生告白、挑选男生一样啦。】

find函数:

bool find(int x){
	int i,j;
	for (j=1;j<=m;j++){    //扫描每个妹子
		if (line[x][j]==true && used[j]==false)      
		//如果互相喜欢并且第x号男生还没有向这个妹子表白过,就尝试表白
		{
			used[j]=1;//不论是否成功,先做标记,该男生对这个妹子表白过(如果这个男生表白成功,used数组自然全部清零,假如对第j个女生没有表白成功,说明第j个女生一定是名花有主而且他的现男友找不到新的女人去代替第j个女生做他的新女友,所以该男生每表白一个女生不论成功与否,接下来被表白的女孩子的现男友都没有必要再去向这些女孩尝试表白了)
			if (girl[j]==0 || find(girl[j])) { 
				//该女生目前还没有男朋友 或者 第x号男生决定与这个女生的现任男朋友谈一谈问问他可不可以换个女生把这个让给自己如果成功就返回1
				girl[j]=x;//这个女生的现任男朋友变成了第x号男生,存储一下,因为女孩子都比较马虎,不记一下可能会忘记...
				return true;//牵手成功返回1
			}
		}
	}
	return false;//牵手失败、没有与任何一个女生确定恋爱关系,返回0
}

主函数:

for (i=1;i<=n;i++)                      //我们鼓励每一个男生依次向自己喜欢的女生表白,且先不管那个女生是否有男朋友...
{
	memset(used,0,sizeof(used));    //对于第i号男生,每一个妹子都还没有表白,所以used[每一个妹子对于他而言]=0
	if find(i) all+=1;              //如果牵手成功,则总情侣对数就+1
}


III流程 是

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

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

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



③其他参考博客:匈牙利算法的BFS和DFS两种思想点击打开链接;匈牙利算法的多种写法点击打开链接;匈牙利算法的理解点击打开链接。


1)

hdu2444,

n 总人数

m 给出互相认识的m对

接下来给出m行,每行是互相认识的两个人的id

请问:能否将所有人分成两个集合,每个集合里的人互相都不认识(是否是二分图?);如果可以,那么如果重新划分,让互相认识的一对人入住一套双人间,最多能入住几套双人间(最大匹配多少)?

//第一次超时,是因为有些数组或者变量没有及时初始化,因为做了个链表进行边到边的存储,所以不及时初始化可能导致某些地方成了循环
#include 
#include 
#include 
#include 

using namespace std;
const int maxn=210*2;
const int maxm=210*210;
int head[maxn];
int used[maxn];
int link[maxm];//匈牙利算法时,判断该点是否已是匹配点
int color[maxn];
int cur=1;
//int spot=0;
struct Edge{
    int from;
    int to;
    int next;
}edges[maxm];
void Add(int f,int t){
    edges[cur].from=f;
    edges[cur].to=t;
    edges[cur].next=head[f];
    head[f]=cur;
    cur++;
}
int Color(int x){//BFS染色 判断是不是二分图
    queue  Q;
    Q.push(x);
    used[x]=1;
    color[x]=1;

    while(!Q.empty()){
        int v_cur=Q.front();
        Q.pop();
        //int color_cur=1;
        for(int e_cur=head[v_cur];e_cur!=-1;e_cur=edges[e_cur].next){
            int v_to=edges[e_cur].to;
            if(used[v_to]==0){
                color[v_to]=1-color[v_cur];//总是让相邻两个点的color一个为1一个为0
                used[v_to]=1;
                Q.push(v_to);
            }
            else if(used[v_to]==1){
                if(color[v_to]==color[v_cur]){
                    return 1;//相邻两点,颜色相同
                }
            }
        }
    }
    return 0;
}
int Dfs_Match(int x){//DFS匈牙利算法 求最大匹配

    for(int e_cur=head[x];e_cur!=-1;e_cur=edges[e_cur].next){
        int v_to=edges[e_cur].to;
        if(used[v_to]==0){
            used[v_to]=1;
            if(link[v_to]==0||Dfs_Match(link[v_to])){//利用了||判断时,前者TRUE就不再进行对后者的判断,的特性
                link[v_to]=x;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        int x,y;
        cur=1;
        memset(edges,0,sizeof(edges));
        //建图:
        memset(head,-1,sizeof(head));
        for(int i=1;i<=m;i++){
            scanf("%d%d",&x,&y);
            Add(x,y);
            Add(y,x);
        }

        //染色法判断是不是二分图
        memset(used,0,sizeof(used));
        memset(color,0,sizeof(color));
            //如果有多个连通图,这样的处理方法只能判断第一个连通图染色情况
        if(Color(1)){
            cout<<"No"<


2)

Description

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
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


你可能感兴趣的:(ACM,图论)