其实我很久之前就想写二分图的匈牙利算法,因为蛋疼的网络流算法写起来很不顺心……而且遇到某些特殊问题当然用特殊方法会有更好的效果啦。
匈牙利算法写起来还是很简单的,基本上理解了交错路之后就OK了。
我用的是邻接表实现。
算法思想:
1.置空res数组,表示全都没有匹配
2.从1到n1找增广路径,如果有的就ans++
3.对于k号找路径的话,就列出所有与k关联的顶点j,筛选出j没有在增广路径或者顶点j已经匹配的但仍然找到增广路径,j的匹配记为k。
/*******************************************************************************/
/* OS : 3.2.0-58-generic #88-Ubuntu SMP Tue Dec 3 UTC 2013 GNU/Linux
* Compiler : g++ (GCC) 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)
* Encoding : UTF8
* Date : 2014-04-06
* All Rights Reserved by yaolong.
*****************************************************************************/
/* Description: ***************************************************************
*****************************************************************************/
/* Analysis: ******************************************************************
*****************************************************************************/
/*****************************************************************************/
#include <iostream>
#include <cstring>
#include<vector>
#include<vector>
#include<cstdio>
using namespace std;
#define N 500
int n1,n2,m; //n1为A集合数,n2为B集合数,m为边数
bool stat[N]; //是否被覆盖
int res[N]; //匹配点
class Vertex
{
public:
Vertex(int n)
{
link_v.resize(n);
}
Vertex()
{
}
vector<int> link_v;
};
vector<Vertex> mp;
bool find(int k)
{
vector<int> ::iterator itr=mp[k].link_v.begin();
vector<int> ::iterator itr_end=mp[k].link_v.end();
while(itr!=itr_end)
{
if(stat[*itr]==0) //未标记=>不在增长路径
{
stat[*itr]=1;
if(res[*itr]==0 //未匹配点
||find(res[*itr])) //还有增长路径
{
res[*itr]=k; //标记*itr =k
return true;
}
}
itr++;
}
return false;
}
int Hungry()
{
int ans=0;
memset(res,0,sizeof(res));
for(int i=1; i<=n1; i++)
{
memset(stat,0,sizeof(stat));
if(find(i)) ans++;
}
return ans;
}
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
int t1,t2;
while(cin>>n1>>n2)
{
mp.clear();
mp.resize(n1+n2+1);
cin>>m;
for (int i=0; i<m; i++)
{
cin>>t1>>t2;
mp[t1].link_v.push_back(t2); //邻接表无向边
mp[t2].link_v.push_back(t1);
}
cout<<Hungry()<<endl;
}
fclose(stdin);
fclose(stdout);
return 0;
}
后来找一道裸题,POJ1274试验了一下,稍微改动了一下输入格式,轻松AC了。
/*******************************************************************************/
/* OS : 3.2.0-58-generic #88-Ubuntu SMP Tue Dec 3 UTC 2013 GNU/Linux
* Compiler : g++ (GCC) 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)
* Encoding : UTF8
* Date : 2014-04-06
* All Rights Reserved by yaolong.
*****************************************************************************/
/* Description: ***************************************************************
*****************************************************************************/
/* Analysis: ******************************************************************
*****************************************************************************/
/*****************************************************************************/
#include <iostream>
#include <cstring>
#include<vector>
#include<vector>
#include<cstdio>
using namespace std;
#define N 500
int n1,n2,m; //n1为A集合数,n2为B集合数,m为边数
bool stat[N];
int res[N];
class Vertex
{
public:
Vertex(int n)
{
link_v.resize(n);
}
Vertex()
{
link_v.clear();
}
vector<int> link_v;
};
vector<Vertex> mp;
bool find(int k)
{
vector<int> ::iterator itr=mp[k].link_v.begin();
vector<int> ::iterator itr_end=mp[k].link_v.end();
while(itr!=itr_end)
{
if(stat[*itr]==0) //未标记=>不在增长路径
{
stat[*itr]=1;
if(res[*itr]==0 //未匹配点
||find(res[*itr])) //还有增长路径
{
res[*itr]=k; //标记*itr =k
return true;
}
}
itr++;
}
return false;
}
int Hungry()
{
int ans=0;
memset(res,0,sizeof(res));
for(int i=1; i<=n1; i++)
{
memset(stat,0,sizeof(stat));
if(find(i)) ans++;
}
return ans;
}
int main()
{
int t1,t2;
while(cin>>n1>>n2)
{
mp.clear();
mp.resize(n1+n2+1);
for(int j=1;j<=n1;j++){
cin>>m;
for (int i=0; i<m; i++)
{
cin>>t1;
mp[n1+t1].link_v.push_back(j);
mp[j].link_v.push_back(n1+t1);
}
}
cout<<Hungry()<<endl;
}
return 0;
}