匈牙利算法本质就是一个个的匹配,对于当前的处理的对象i,假设他的匹配对象为j,但是j已经和v匹配好了,那么就让v去找找着能不能和别人匹配
v可以和别人匹配则i与j匹配,不能则i去找别人匹配
另外引入几个定义和结论:
1:最大匹配数 + 最大独立集 = n + m(n,m为二分图两边的节点数)
2:二分图的最小顶点覆盖数 = 最大匹配数
3:最小路径覆盖 = 最大独立集
最大独立集是指:求一个二分图中最大的一个点集,该点集内的点互不相连。
最小顶点覆盖是指: 在二分图中,用最少的点,让所有的边至少和一个点有关联。
最小路径覆盖是指:
对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。
最大匹配是指:
对于二分图的所有边,寻找一个子集,这个子集满足两个条件,
1:任意两条边都不依赖于同一个点。
2:让这个子集里的边在满足条件一的情况下尽量多。
算法的证明如下:http://blog.csdn.net/yuxue_23/article/details/12224067
然后是模版:
bool find(int x){ int i,j; for (j=1;j<=m;j++){ //扫描每个妹子 if (line[x][j]==true && used[j]==false) //如果有暧昧并且还没有标记过(这里标记的意思是这次查找曾试图改变过该妹子的归属问题,但是没有成功,所以就不用瞎费工夫了) { used[j]=1; if (girl[j]==0 || find(girl[j])) { //名花无主或者能腾出个位置来,这里使用递归 girl[j]=x; return true; } } } return false; } int main(){ for (i=1;i<=n;i++) { memset(used,0,sizeof(used)); //这个在每一步中清空 if find(i) all+=1; } return 0; }
然后是本题代码:
#include <cstdio> #include <cstring> #include <vector> #include <algorithm> #include <iostream> #include <map> #include <queue> #include <stack> #include <cmath> //#pragma comment(linker, "/STACK:102400000,102400000") using namespace std; #define PF(x) cout << "debug: " << x << " "; #define EL cout << endl; #define PC(x) puts(x); typedef long long ll; #define CLR(x, v) sizeof (x, v, sizeof(x)) using namespace std; const int INF = 0x5f5f5f5f; const int N= 1000050; const int maxn=110; int n,m,k,used[maxn],lk[maxn][maxn],mark[maxn]; bool find(int u){ for(int i = 1;i <= m;i++){ if(lk[u][i]&&!used[i]){ used[i] = 1; if(mark[i] == 0||find(mark[i])){ mark[i] = u; return true; } } } return false; } int main() { //freopen("in.txt","r",stdin); while(~scanf("%d",&n)){ if(n == 0) break; memset(lk,0,sizeof(lk)); memset(mark,0,sizeof(mark)); scanf("%d%d",&m,&k); int i,a,b,c; for(i = 1;i <= k;i++){ scanf("%d%d%d",&c,&a,&b); lk[a][b] = 1; } int ans=0; for(i = 1;i <= n;i++){ memset(used,0,sizeof(used)); if(find(i)) ans++; } cout<<ans<<endl; } return 0; }