匈牙利算法,求二分图最大匹配。
若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 }
时间复杂度是O(VE),空间复杂度感觉O(V)就行了啊,为什么其他人都说是O(V+E)?.