匈牙利算法

匈牙利算法

算法用途:用于求最大匹配数,比如n个男生和m个女生,每个人都有暗恋的对象(一个人可能暗恋多个人)求能凑够的情侣的数量。

算法思想主要利用深度优先搜索(DFS)来实现。比如男生A喜欢女生B,但是女生B已经有了男朋友C。那就在男生C的暗恋对象中找到一个女生D,让C与D成为情侣,那么这样女生B就没有男朋友了,于是A和B就可以成为情侣。在这里插入图片描述
在本图中,A<->B、A<->C、B<->E、C<->G,他们相互喜欢。我们先匹配A,发现A可以与B组成情侣;我们继续看E,发现E喜欢的B已经有情侣了,那么就让B的情侣A去另找情侣,发现A又可以找到C,于是让A与C成为情侣,进而E就可以和B在一起了;继续看G,我们发现G喜欢的C已经有情侣A了,于是我们企图让A再去找其他的情侣,于是又找到了B,企图和B在一起,但是发现B也有情侣E,那么再尝试找E能否和未匹配的人在一起,发现不可以,而且路已经走到了尽头,所以一路返回,告知G没有人可以与他在一起,匹配失败。故本图的最大匹配对数就是2对。

算法步骤:
①:建立关系,也就是所谓的相互暗恋关系
②:遍历每个人(也可以只遍历男生或只遍历女生),尝试能否找到伴侣,找到就让答案+1
③:也就是寻找函数,此函数运用深度优先遍历进行回溯遍历,并且在过程中记录遍历到人的当前的情侣是谁

核心算法:

int dfs(int x) {//注意深搜每个人时,都是要memset数组v
	int i;
	for(i=0;i<vt[x].size();i++) {
		int y = vt[x][i];
		if(!v[y]) {
			v[y] = 1;
			if(father[y]==-1||dfs(father[y])) {//如果喜欢的人没有还单身(也就是father[y]等于-1)
			//那就直接建立联系,否则dfs喜欢的人的情侣,继续深搜。
				father[y] = x;//建立联系
				return 1;
			}
		}
	}
	return 0;
}

经典例题Swap Free
题目大意:给n个字符串,长度相同,都由相同个数的每个字母组成。求选择最大的m个字符串组成一个集合,使他们不能两两互相转化(转化是指将字符串A中的两个字符交换位置可以的到B)

思路:重点在于如何将问题落实在算法上。既然要求不能两两互相转化。那么我们就先求能两两互相转化的字符串的个数为cnt,那么cnt/2个字符串一定是不能两两转化的。那么本身还有n-cnt个孤立点(不可能与其他任何n-1个点相互转化)那么答案就是n-cnt+cnt/2了。那么只用在可以转化的两个字符串之间建立联系(也就是相互暗恋的关系),然后跑一个匈牙利算法求出最大匹配数即可。

题目代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 550;

int v[N],father[N];
vector<int> vt[N];
vector<string> vet;

int is(int x,int y) {
	int i;
	string s = vet[x],s1 = vet[y];
	int l = s.size();
	int sum = 0;
	for(i=0;i<l;i++) {
		if(s[i]!=s1[i])sum++;
	}
	if(sum==2)return 1;
	return 0;
}
int dfs(int x) {
	int i;
	for(i=0;i<vt[x].size();i++) {
		int y = vt[x][i];
		if(!v[y]) {
			v[y] = 1;
			if(father[y]==-1||dfs(father[y])) {
				father[y] = x;
				return 1;
			}
		}
	}
	return 0;
}
int main() {
	string x;
	int n,m,i,j,k;
	cin >> n;
	for(i=0;i<n;i++) {
		cin >> x;
		vet.push_back(x);
	}	
	for(i=0;i<n;i++) {
		for(j=i+1;j<n;j++) {
			if(is(i,j)) {
				vt[i].push_back(j);
				vt[j].push_back(i);
			}
		}
	}
	int ans = 0;
	memset(father,-1,sizeof father);
	for(i=0;i<n;i++) {
		memset(v,0,sizeof v);
		if(dfs(i))ans++;
	}
	printf("%d\n",n-(ans)/2);
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值