匈牙利算法
算法用途:用于求最大匹配数,比如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;
}