二分图最大匹配,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;
}

话说,有重边的有木有,,,没注意。错了几次。。。。。。