Poj1469_匈牙利算法_最大二分匹配
这道题是一个基本的匈牙利算法,求最大二分匹配。之前在算法导论中学习网络流,明白了网络流中的残留网络,增光路和割,但是去模拟书上的伪代码感觉很是费力。就打算从最基本的匈牙利算法做起。
下面先介绍一下匈牙利算法:
该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最大匹配的算法:
匹配是边集的子集。
设M是图G的一个匹配,需要掌握一下几个概念:
1.M-交错路:M-交错路是一条通路,这条通路中的边为属于M与不属于M但属于G的边交替出现。
2.M-饱和点:M-饱和点是与M中的某条边相关联的点。否则是非M-饱和点。
3.M-可增广路:M-交错路的起点和终点都是非M-饱和点。
设P是M-可增广路,则可以得出以下三个结论:
1.p的路径个数为奇数,并且首尾两条边是不属于M
2.p取反可以得到更大的匹配。
3.M是最大匹配当且仅当不存在M的增广路径。
所以算法就是不断的找出增广路径,知道找不出为止。
这道题我用的数据结构是二维数组,这样的话,设n为人数,p为课程数.
空间复杂度为O(n^2),而时间复杂度为o(p*n*n),其中最坏情况下寻找增广路的时间复杂度为o(n)。
以后再给出用邻接表的分析。
需要注意的是,读题的时候以为课程数和学生数要相等,但后来仔细读题发现Each student visits zero, one or more than one courses.说明可以有学生不匹配某个课程。这样看来结果就清楚了,得出的最大二分匹配与课程数比较即可。
还有就是这么多数据的读入,用cin会超时的,之前也遇到这样的问题,好在这次没有在这上面出bug。
以下是代码:
1 #include <iostream> 2 #include <string> 3 #include <memory.h> 4 #include <stdio.h> 5 using namespace std; 6 bool array[101][301]; 7 int res[301]; 8 bool use[301]; 9 int p,n; 10 11 bool find(int i) 12 { 13 int j; 14 for(j=1;j<=n;j++) 15 { 16 if(array[i][j] && !use[j]) 17 { 18 use[j]=true; 19 if(res[j]==0 || find(res[j])) 20 { 21 res[j]=i; 22 return true; 23 } 24 } 25 } 26 return false; 27 } 28 29 int main() 30 { 31 int num; 32 scanf("%d",&num); 33 int i,j,m,temp; 34 int result; 35 while(num--) 36 { 37 memset(array,0,sizeof(array)); 38 memset(res,0,sizeof(res)); 39 scanf("%d%d",&p,&n); 40 for(i=1;i<=p;i++) 41 { 42 scanf("%d",&m); 43 for(j=1;j<=m;j++) 44 { 45 scanf("%d",&temp); 46 array[i][temp]=true; 47 } 48 } 49 50 result=0; 51 for(i=1;i<=p;i++) 52 { 53 memset(use,false,sizeof(use)); 54 if(find(i)) 55 result++; 56 } 57 if(result==p) 58 printf("YES\n"); 59 else 60 printf("NO\n"); 61 } 62 return 0; 63 }
这个题也是tju oj1050
这个题有一个小小的优化:
题目要求找对课程的完全匹配,当对课程进行遍历的时候,如果某个课程找匹配失败,就可以结束遍历。
1 for(i=1;i<=p;i++) 2 { 3 memset(use,false,sizeof(use)); 4 if(!find(i)) 5 { 6 printf("NO\n"); 7 break; 8 } 9 } 10 if(i==p+1) 11 printf("YES\n");