【网络流24题】最小路径覆盖

LOJ 6002 【网络流24题】最小路径覆盖

题面

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

设计一个有效算法求一个有向无环图 \(G\) 的最小路径覆盖,并输出每条路径。

题解

由于每个点只能用一次,把每个点拆成“入点”和“出点”。如果原图中有一条有向边\(u \rightarrow v\),那么将\(u\)的出点与\(v\)的入点相连。这样我们建出了一个二分图。
总点数 - 二分图匹配数 = 最小路径覆盖。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
#define space putchar(' ')
#define enter putchar('\n')
template <class T>
bool read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
        if(c == '-') op = 1;
        else if(c == EOF) return 0;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
        x = x * 10 + c - '0';
    if(op) x = -x;
    return 1;
}
template <class T>
void write(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}

const int N = 405, M = 20005, INF = 0x3f3f3f3f;
int ncnt, n, m, s, t, ans;
int ecnt = 1, adj[N], nxt[M], go[M], cap[M], cur[N];
int que[N], qr, lev[N], stk[N], top;

void ADD(int u, int v, int w){
    go[++ecnt] = v;
    nxt[ecnt] = adj[u];
    adj[u] = ecnt;
    cap[ecnt] = w;
}
void add(int u, int v, int w){
    ADD(u, v, w), ADD(v, u, 0);
}
bool bfs(){
    for(int i = 1; i <= ncnt; i++)
        lev[i] = -1, cur[i] = adj[i];
    lev[s] = 0, que[qr = 1] = s;
    for(int ql = 1; ql <= qr; ql++){
        int u = que[ql];
        for(int e = adj[u], v; e; e = nxt[e])
            if(cap[e] && lev[v = go[e]] == -1){
                lev[v] = lev[u] + 1, que[++qr] = v;
                if(v == t) return 1;
            }
    }
    return 0;
}
int dinic(int u, int flow){
    if(u == t) return flow;
    int delta, ret = 0;
    for(int &e = cur[u], v; e; e = nxt[e])
        if(cap[e] && lev[v = go[e]] > lev[u]){
            delta = dinic(v, min(cap[e], flow - ret));
            if(delta){
                cap[e] -= delta;
                cap[e ^ 1] += delta;
                ret += delta;
                if(ret == flow) return flow;
            }
        }
    lev[u] = -1;
    return ret;
}
int main(){
    read(n), read(m);
    s = 2 * n + 1, t = 2 * n + 2;
    ncnt = 2 * n + 2;
    for(int i = 1; i <= n; i++)
        add(s, i, 1), add(i + n, t, 1);
    for(int i = 1, u, v; i <= m; i++){
        read(u), read(v);
        add(u, v + n, 1);
    }
    while(bfs()) ans += dinic(s, INF);
    for(int e = adj[s], u; e; e = nxt[e]){
        if(!(e & 1) && cap[e]){
            u = go[e];
            stk[top = 1] = u;
            for(int i = 1; i <= top; i++){
                bool flag = 0;
                u = stk[i] + n;
                for(int E = adj[u]; E; E = nxt[E])
                    if((E & 1) && cap[E]){
                        flag = 1;
                        stk[++top] = go[E];
                        break;
                    }
                if(!flag) break;
            }
            for(int i = top; i; i--)
                write(stk[i]), space;
            enter;
        }
    }
    write(n - ans), enter;
    return 0;
}
posted @ 2017-11-16 21:39  胡小兔  阅读(197)  评论(0编辑  收藏  举报