首页 > 代码库 > graph | hungary
graph | hungary
匈牙利算法,求二分图最大匹配。
若P是图G中一条连通两个未匹配顶点的路径,并且属于M的边和不属于M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径。(M为一个匹配)
由增广路的定义可以推出下述三个结论:
- P的路径长度必定为奇数,第一条边和最后一条边都不属于M。所以Line 25-27从first part出发,不从二分图的另一部分出发。Line 12实现了交替出现的逻辑;node->neig匹配,当且仅当neig没有被其他点匹配,或者neig被first中的其他点matches[neig]匹配,并且从matches[neig]能够找到一条增广路径。这里就实现了交替的逻辑了。
- 将M和P进行异或操作(去同存异)可以得到一个更大的匹配M’。这是因为,属于M的边和不属于M的边交替出现,且第一和最后一条边都不属于M,所以增广路径中,不属于M的边比属于M的边多1,去同存异之后,一定会得到一个更大的匹配(加1了)。Line 13实现的是去同存异的逻辑。如果从node到neig存在一条增广路径,那么中间这些相同的部分直接省略。
- M为G的最大匹配当且仅当不存在M的增广路径。
1 #include <iostream> 2 #include <cstdio> 3 #include <vector> 4 5 using namespace std; 6 7 bool augment(vector<vector<int> > &adj, int node, 8 vector<bool> &visited, vector<int> &matches) { 9 for (auto neig : adj[node]) {10 if (!visited[neig]) {11 visited[neig] = true;12 if (matches[neig] == -1 || augment(adj, matches[neig], visited, matches)) {13 matches[neig] = node;14 return true;15 }16 }17 }18 return false;19 }20 21 int hungary(vector<vector<int> > &adj, vector<int> &first) {22 vector<bool> visited;23 vector<int> matches(adj.size(), -1); 24 int count = 0;25 for (auto f : first) {26 visited.assign(adj.size(), false);27 if (augment(adj, f, visited, matches)) {28 count++;29 }30 }31 for (int i = 0; i < adj.size(); ++i) {32 cout << i << "<->" << matches[i] << endl;33 }34 return count;35 }36 37 int main(int argc, char** argv) {38 freopen("input.txt", "r", stdin);39 int first, n, m;40 cin >> n >> first >> m;41 vector<vector<int> > adj(n);42 vector<int> left;43 for (int i = 0; i < first; ++i) {44 int l;45 cin >> l;46 left.push_back(l);47 }48 for (int i = 0; i < m; ++i) {49 int n1, n2;50 cin >> n1 >> n2;51 adj[n1].push_back(n2);52 adj[n2].push_back(n1);53 }54 55 cout << hungary(adj, left) << endl;56 return 0;57 }
假设左边结点个数为n, 右边结点个数为m,这里用了visited来确保右边的点不会重复访问,时间复杂度还是是O(nmn),如果把邻接表用list而不是vector的话,访问过的右边结点删去,那么开销可以在O(nm)了。因为右边结点只需访问一次。
空间复杂度是O(m+n)。
graph | hungary
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。