ac图轮之匈牙利算法--月老的难题

匈牙利算法

给一个二分图,然后给出两个顶点集合中的和可匹配的关系,那么要求更可能多的使得每个点都在另一个集合中有一对一的对应,那么我们可以利用匈牙利算法进行求得。

这里给出匈牙利算法的具体算法是回溯所以有可能会造成超时和暴栈,谨慎使用!

一下是csdn某个大佬的图解

通过数代人的努力,你终于赶上了剩男剩女的大潮,假设你是一位光荣的新世纪媒人,在你的手上有N个剩男,M个剩女,每个人都可能对多名异性有好感(惊讶-_-||暂时不考虑特殊的性取向),如果一对男女互有好感,那么你就可以把这一对撮合在一起,现在让我们无视掉所有的单相思(好忧伤的感觉快哭了),你拥有的大概就是下面这样一张关系图,每一条连线都表示互有好感。


本着救人一命,胜造七级浮屠的原则,你想要尽可能地撮合更多的情侣,匈牙利算法的工作模式会教你这样做:

===============================================================================

 先试着给1号男生找妹子,发现第一个和他相连的1号女生还名花无主,got it,连上一条蓝线


===============================================================================

接着给2号男生找妹子,发现第一个和他相连的2号女生名花无主,got it


===============================================================================

接下来是3号男生,很遗憾1号女生已经有主了,怎么办呢?

我们试着给之前1号女生匹配的男生(也就是1号男生)另外分配一个妹子。

(黄色表示这条边被临时拆掉)

与1号男生相连的第二个女生是2号女生,但是2号女生也有主了,怎么办呢?我们再试着给2号女生的原配(发火发火)重新找个妹子(注意这个步骤和上面是一样的,这是一个递归的过程)


此时发现2号男生还能找到3号女生,那么之前的问题迎刃而解了,回溯回去

2号男生可以找3号妹子~~~                  1号男生可以找2号妹子了~~~                3号男生可以找1号妹子

所以第三步最后的结果就是:


===============================================================================

 接下来是4号男生,很遗憾,按照第三步的节奏我们没法给4号男生出来一个妹子,我们实在是无能为力了……香吉士同学走好。

===============================================================================

这就是匈牙利算法的流程,其中找妹子是个递归的过程,最最关键的字就是“ ”字

我总结下,就是在主函数中遍历每个男生,对于每一个男生需要做如下的操作:

1.对于每个男生我遍历所有的女生对所有的女生进行扫描

2.如果这个女生没有被访问过并且这个男生喜欢这个女生,那么就设置这个女生被访问过,然后呢,进行扫描这个女生的男朋友

如果这个女生的男朋友没有或者是说这个男朋友喜欢的女生中有其他的选择(递归他的男朋友),那么就让他的男朋友腾出这个女生给当前的男生,如果这个女朋友即是名花有主有是人家男朋友没有其他的女生可以喜欢(有可能是这个男朋友的其他女生没有了,也有可能是这个男朋友的其他喜欢的女生全有男朋友了),那么这个女生不可以匹配到当前的女生,就扫面下一个

3.如果在遍历的过程中找到了,就返回,但是呢,这个女生的男朋友要标记成当前男生,以便后来的男生参考

4.如果在所有的女生都没有可能,那么就搞基吧...

5.返回主函数如果该男生可以找到女生,那么匹配关系数目+1

6.便利完所有的男生之后,我们就可以输出匹配数目了,并且这个匹配关系数目就是最大的

分析下这个算法>O(n^2)的,没办法,这是相当暴力搜索了,在保证每个女生已经找到了男朋友不会变成单身的情况下(有备胎的可以选择的情况下),给一些男生腾出来当前的女朋友....

月老的难题

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 4
描述

月老准备给n个女孩与n个男孩牵红线,成就一对对美好的姻缘。

现在,由于一些原因,部分男孩与女孩可能结成幸福的一家,部分可能不会结成幸福的家庭。

现在已知哪些男孩与哪些女孩如果结婚的话,可以结成幸福的家庭,月老准备促成尽可能多的幸福家庭,请你帮他找出最多可能促成的幸福家庭数量吧。

假设男孩们分别编号为1~n,女孩们也分别编号为1~n。

输入
第一行是一个整数T,表示测试数据的组数(1<=T<=400)
每组测试数据的第一行有两个整数n,K,其中男孩的人数与女孩的人数都是n。(n<=500,K<=10 000)
随后的K行,每行有两个整数i,j表示第i个男孩与第j个女孩有可能结成幸福的家庭。(1<=i,j<=n)
输出
对每组测试数据,输出最多可能促成的幸福家庭数量
样例输入
1
3 4
1 1
1 3
2 2
3 2
样例输出
2


第一次我是利用邻接矩阵弄的,导致匈牙利算法内部循环为n,导致超时

#include<iostream>
#include<string.h>
using namespace std;
#define MAXN 505//初始化最大的男生或者是女生的数目
int graph[MAXN][MAXN];//建立邻接矩阵
int vis[MAXN];//标记女生是否被访问过的数组,用来剪枝
int girls[MAXN];//标记女生的男朋友
int n;//标记数目
bool Find(int x)//匈牙利算法,参数男生就是要匹配的男生
{
	for(int i=1;i<=n;i++)//遍历所有的女生
	{
		if(graph[x][i]>0&&vis[i]==0)//如果当前的女生与这个男生有相互喜欢的关系,并且这个女生没有被分配
		{
			vis[i]=1;//那么标记访问过
			if(girls[i]==0||Find(girls[i]))//如果这个女生没有男朋友或者这个女生有男朋友并且有备胎
			{
				girls[i]=x;//那么这个女生就匹配这个男生
				return true;//匹配成功就返回
			}
		}
	}
	return false;//所以的女生没有可能,搞基
}
int main()
{
	int ncase;
	cin>>ncase;
	while(ncase--)
	{
		int k;
		memset(graph,0,sizeof(graph));//初始化图,即任意男女都没有喜欢关系
		cin>>n>>k;
		for(int i=0;i<k;i++)
		{
			int v,u;
			cin>>v>>u;
			graph[v][u]=1;//如果喜欢就标记为1
		}
		memset(girls,0,sizeof(girls));//初始化所以的女生的男朋友的都没有
		int cnt=0;//初始化匹配数目为0
		for(int i=1;i<=n;i++)//遍历所以的男生
		{
			memset(vis,0,sizeof(vis));//初始化vis
			if(Find(i))//寻找当前男生是否可以找到女生
				cnt++;//成功匹配就+1
		}
		cout<<cnt<<endl;//输出答案
	}
	return 0;
}

第二次我用邻接表,匈牙利内部的循环就变成了当前男生备胎的数目

#include<iostream>
#include<string.h>
#include<vector>
using namespace std;
#define MAXN 505
int vis[MAXN];//访问标记数组,用来剪枝
vector<int> g[MAXN];//vecto数组用来充当邻接表
int girls[MAXN];//女孩子的男朋友
int n;//标记匹配的男生或者是女生的人数
bool Find(int x)//匈牙利算法,参数x表示编号是x的男生找女朋友
{
    for(int i=0;i<g[x].size();i++)//邻接表的优化,把n又换成g[x].size();
    {
        int t=g[x][i];//获取当前男生的每个女朋友
        if(vis[t]==0)//如果当前女生没有被访问过
        {
            vis[t]=1;//标记访问
            if(girls[t]==0||Find(girls[t]))//如果这个女生没有男朋友或者是递归查找这个女生的男朋友有备胎可以选择
            {
                girls[t]=x;//那么这个女生就给这个男生分配
                return true;//一旦分配了就返回
            }
        }
    }
    return false;//如果最后也没有成功分配,那么就返回false
}
int main()
{
    ios::sync_with_stdio(false);
    int ncase;
    cin>>ncase;
    while(ncase--)
    {
        int k;
        for(int i=0;i<MAXN;i++)
            g[i].clear();//初始化每个男生的喜欢的女孩子的编号为空
        cin>>n>>k;
        for(int i=0;i<k;i++)
        {
            int v,u;
            cin>>v>>u;
            g[v].push_back(u);//加入,建图
        }
        memset(girls,0,sizeof(girls));//初始化所有女生的男朋友没有
        int cnt=0;//男生匹配成功的数目为0
        for(int i=1;i<=n;i++)//遍历每一个男生
        {
            memset(vis,0,sizeof(vis));//每次深搜前都初始化所以的女生都没有被访问过
            if(Find(i))//如果当前男生成功被匹配
                cnt++;//成功数目+1
        }
        cout<<cnt<<endl;//输出最终的答案
    }
    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值