二分图最大匹配,HK算法,暴力枚举
题意:
分析:
这道题是很明显是二分匹配题目。但因为我初学二分图所以刚开始并没有看出如何建立二分图的。
我是想,对于人:1,2,3,4,5,6,7,8
在刚开始匹配,1去将他手中的一个任务交给5
然后到2开始交任务,如果2也有一个要交给5的任务,那么此时他发现5已经有任务了!
所以,我们要看1,能不能将5的任务收回找别人交?
其实,这就是一个寻找增广路的过程!!!所以,二分图的做法自然就出来了。
我们建立二分图:
1 1
2 2
3 3
4 4
5 5
如上所示。
然后,算他的最大匹配就ok了。
但是,要判断哪些点是必须不能去掉的点却好麻烦。。。我不会强连通分量,所以只能暴力枚举。。。。。
依次去掉 一个边看最大匹配有没有变化。。。。。我是废物,,
因为我选择枚举,所以时间复杂的直接高了一个次方。再用匈牙利算法就不太保险了。但似乎也能过?
我用的HK算法。总复杂度为O(n^2.5)
n最大为500故n^2.5 = * 10^6
还不到10^7,勉勉强强。
代码如下:
#include<iostream> #include<algorithm> #include<vector> #include<queue> #include<map> using namespace std; typedef pair<int, int> pii; const int inf = 2e9; const int max_n = 510; const int max_m = 20010; struct edge { int to, next; }E[max_m * 2]; int head[max_n]; int cnt = 1; int n, m; void add(int from, int to) { E[cnt].to = to; E[cnt].next = head[from]; head[from] = cnt++; } int eu = -1, ev = -1; int distright[max_n], distleft[max_n]; int rightTo[max_n], leftTo[max_n]; int dist; bool searchpath() { fill(distright, distright + 1 + n, -1); fill(distleft, distleft + 1 + n, -1); queue<int> que;dist = inf; for (int i = 1;i <= n;i++) if (rightTo[i] == -1)que.push(i); while (!que.empty()) { int u = que.front();que.pop(); if (distright[u] > dist)break; for (int i = head[u];i;i = E[i].next) { int v = E[i].to; if (u == eu && v == ev)continue; if (distleft[v] != -1)continue; distleft[v] = distright[u] + 1; if (leftTo[v] == -1)dist = distleft[v]; else { distright[leftTo[v]] = distleft[v] + 1; que.push(leftTo[v]); } } }return dist != inf; } bool matchpath(int u) { for (int i = head[u];i;i = E[i].next) { int v = E[i].to; if (u == eu && v == ev)continue; if (distleft[v] != distright[u] + 1)continue; if (distleft[v] == dist && leftTo[v] != -1)continue; distleft[v] = -1; if (leftTo[v] == -1 || matchpath(leftTo[v])) { leftTo[v] = u;rightTo[u] = v; return true; } }return false; } int HK() { fill(leftTo, leftTo + 1 + n, -1); fill(rightTo, rightTo + 1 + n, -1); int ans = 0; while (searchpath()) { for (int i = 1;i <= n;i++) { if (rightTo[i] == -1 && matchpath(i)) ans++; } } return ans; } int main() { ios::sync_with_stdio(0); cin >> n >> m; fill(head, head + n + 1, false); map<pii, int>m1;map<pii, int>m2; for (int i = 1;i <= m;i++) { int u, v;cin >> u >> v; pii p = { u,v }; m2[p]++; if (m2[p] > 1)continue; add(u, v);m1[p] = i; } int ans = HK(); vector<pii> es; for (int i = 1;i <= n;i++) { if (rightTo[i] == -1)continue; es.push_back({ i,rightTo[i] }); } vector<int> res; for (int i = 0;i < es.size();i++) { eu = es[i].first;ev = es[i].second; pii p = { eu,ev }; if (m2[p] > 1)continue; if (HK() != ans) res.push_back(m1[p]); } cout << ans << " " << res.size() << endl; sort(res.begin(), res.end()); for (int i = 0;i < res.size();i++) cout << res[i] << endl; }
话说,有重边的有木有,,,没注意。错了几次。。。。。。