[网络流24题] 最小路径覆盖问题(匈牙利 最大流)

题目链接

定理: 最小路径覆盖数=|G|-二分图最大匹配数

所以匈牙利算法就来了,内置匹配,简单易上手

#include <bits/stdc++.h>
const int maxn = 310;
using namespace std;
typedef long long ll;

vector<int> maps[maxn];
int pipei[maxn], n, m;
bool mark[maxn];
int in[maxn], son[maxn];

bool dfs(int u) //找增广路径,匹配
{
    for (int i = 0, len = maps[u].size(); i < len; i++){
        int v = maps[u][i];
        if (!mark[v]){
            mark[v] = true;
            if (!pipei[v] || dfs(pipei[v])){
                pipei[v] = u;
                return true;
            }
        }
    }
    return false;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++){
        int a, b;
        scanf("%d%d", &a, &b);
        maps[a].push_back(n + b);   //建图
    }
    int ans = 0;
    for (int i = 1; i <= n; i++){
        memset(mark, false, sizeof(mark));
        if (dfs(i))
            ans++;
    }
    for (int i = n + 1; i <= 2 * n; i++){
        if (pipei[i]){
            int u = pipei[i], v = i - n;
            son[u] = v; //u --> v
            in[v]++;    //入度
        }
    }
    for (int i = 1; i <= n; i++){   //输出答案
        if (in[i] == 0){
            int u = i;
            printf("%d", u);
            while ((u = son[u]))
                printf(" %d", u);
            printf("\n");
        }
    }
    printf("%d\n", n - ans); //最小路径覆盖数=|G|-二分图最大匹配数
    getchar();
    getchar();
    return 0;
}

好吧,从二分图,入手用匈牙利算法,好像就是个模板题了

最大流:用最大流的话,主要还是建图,还有反向弧为满流,就是匹配边

#include <bits/stdc++.h>
const int maxn = 160;
const int emax = 6000 * 3;
const int inf = 0x3f3f3f3f;
using namespace std;
typedef long long ll;

struct note{
    int u, v, w;
    int next;
} e[emax];
int head[maxn * 2], cnt;
int level[maxn * 2], son[maxn], in[maxn];
int n, m, s, t;

void add(int u, int v, int w){
    e[cnt].u = u, e[cnt].v = v, e[cnt].w = w;
    e[cnt].next = head[u], head[u] = cnt++; //正向弧

    e[cnt].u = v, e[cnt].v = u, e[cnt].w = 0;
    e[cnt].next = head[v], head[v] = cnt++; //反向弧
}

bool bfs(){
    memset(level, -1, sizeof(level));
    level[s] = 1;
    queue<int> q;
    q.push(s);
    while (!q.empty()){
        int u = q.front();
        q.pop();
        for (int i = head[u]; ~i; i = e[i].next){
            int v = e[i].v;
            if (e[i].w > 0 && level[v] == -1){
                level[v] = level[u] + 1;
                q.push(v);
            }
        }
    }
    return level[t] != -1;
}

int dfs(int u, int delta){
    if (u == t)
        return delta;
    int flow = 0;
    for (int i = head[u]; ~i; i = e[i].next){
        int v = e[i].v;
        if (e[i].w > 0 && level[u] + 1 == level[v]){
            int tmp = dfs(v, min(delta - flow, e[i].w));
            e[i].w -= tmp;
            e[i ^ 1].w += tmp;
            flow += tmp;
        }
    }
    if (flow == inf)
        level[u] = -1;
    return flow;
}

int Dinic()
{
    int maxflow = 0, tmp;
    while (bfs()){
        while ((tmp = dfs(s, inf)))
            maxflow += tmp;
    }
    return maxflow;
}

int main()
{
    scanf("%d%d", &n, &m);
    s = 0, t = 2 * n + 1;
    memset(head, -1, sizeof(head));
    for (int i = 0; i < m; i++){
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b + n, 1);   //建边
    }
    for (int i = 1; i <= n; i++)
        add(s, i, 1);   //建边
    for (int i = n + 1; i <= 2 * n; i++)
        add(i, t, 1);   //建边

    int ans = Dinic();
    for (int i = 0; i < cnt; i += 2)
    {
        if (e[i].u == s || e[i ^ 1].u == t)
            continue;
        if (e[i ^ 1].w){ //反向弧
            son[e[i].u] = e[i].v - n;
            in[e[i].v - n]++;
        }
    }
    for (int i = 1; i <= n; i++){
        if (in[i] == 0){
            int u = i;
            printf("%d", u);
            while ((u = son[u]))
                printf(" %d", u);
            printf("\n");
        }
    }
    printf("%d\n", n - ans);
    return 0;
}

好吧,其实如果,做过类似,最大流去解决二分图匹配的话,又变成模板题了

posted @ 2019-07-28 21:03  季之怡  阅读(17)  评论(0编辑  收藏  举报