[网络流24题]P2764 最小路径覆盖

拆点,最大流
https://www.luogu.com.cn/problem/P2764

题意

给定有向图 \(G=(V,E)\) 。设 \(P\)\(G\) 的一个简单路(顶点不相交)的集合。如果 \(V\) 中每个定点恰好在 \(P\) 的一条路上,则称 \(P\)\(G\) 的一个路径覆盖。\(P\) 中路径可以从 \(V\) 的任何一个定点开始,长度也是任意的,特别地,可以为 \(0\)\(G\) 的最小路径覆盖是 \(G\) 所含路径条数最少的路径覆盖。设计一个有效算法求一个 \(DAG,\ G\) 的最小路径覆盖。

Tutorial

每个点原本都是一条路径,如果用边将两个点连接起来,那么就会减少一条路径,这很符合匹配的概念。于是将每个点拆成入点和出点,对于边 \((u,v)\),连接 \((u_出, v_入)\), 显然这是一个二分图。建完图之后跑最大匹配。匹配就代表着选择这条路径。最后再利用link输出路径即可。
经典拆点了。

点击查看代码
#include <bits/stdc++.h>
typedef long long ll;
#define endl '\n'
#define P pair<int, int>
#define eps 1e-8
#define IOS                  \
    ios::sync_with_stdio(0); \
    cin.tie(0);              \
    cout.tie(0);
using namespace std;

const int N = 150 + 5;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int n, m;
vector<int> mp[N];
int vis[N], link[2][N], pre[N];
int dfs(int u) {
    for (auto v : mp[u]) {
        if (vis[v])
            continue;
        vis[v] = 1;
        if (link[0][v] == 0 || dfs(link[0][v])) {
            link[0][v] = u;
            link[1][u] = v;
            return 1;
        }
    }
    return 0;
}

int main() {
    cin >> n >> m;
    for (int i = 0, u, v; i < m; i++) {
        cin >> u >> v;
        mp[u].push_back(v);
    }

    for (int i = 1; i <= n; i++) {
        memset(vis, 0, sizeof vis);
        dfs(i);
    }

    // for(int i=1;i<=n;i++){
    //     cout<<i<<": "<<link[1][i]<<" "<<link[0][i]<<endl;
    // }
    int tot = 0;
    memset(vis, 0, sizeof vis);
    for (int i = 1; i <= n; i++) {
        int o = i, f = 1, flag = 0;
        while (o && !vis[o]) {
            flag = 1;
            cout << o << " ";
            vis[o] = 1;
            o = link[f][o];
        }

        if (flag)
            flag = 0, tot++, cout << endl;
    }
    cout << tot;
}
posted @ 2021-10-19 00:03  FushimiYuki  阅读(43)  评论(0)    收藏  举报