[LOJ.AC]#6002. 「网络流 24 题」最小路径覆盖

建图

最小路径覆盖问题可以转化为最大二分图匹配问题

现在已知 G(V,E) G ( V , E ) ,求最小路径覆盖,做法:

  • 建立空的网络 N(V,E) N ( V ′ , E ′ )
  • 取源点 s s 汇点 t t ,添加到 V V ′
  • vV ∀ v ∈ V 都添加到 V V ′
  • 对应 vV ∀ v ∈ V 都新建顶点 v v ′ V V ′ ,并添加容量为一的边 (s,v),(v,t) ( s , v ) , ( v ′ , t ) E E ′
  • 对应 (u,v)E ∀ ( u , v ) ∈ E ,都添加容量为一的边 (u,v) ( u , v ) E E ′

可以发现除去 源汇点 的 N(V,E) N ( V ′ , E ′ ) 就是二分图

求出 N(V,E) N ( V ′ , E ′ ) 的 max_flow(s,t),那么最小路径覆盖 = |V| | V | − max_flow(s,t)

code

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//#include 
//#include 
#include 
using namespace std;

#define mp make_pair
#define pb push_back
#define se second
#define fi first
#define rep(i, n) for(int i = 0; i < n; ++i)

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<int, int> pii;

const int MOD = (int)1e9 + 7;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const db PI = acos(-1.0);
const db EPS = 1e-10;

const int MAXV = 405;
const int MAXE = 6005 * 3;

int V, E;

struct edge{
    int to, cap, cost, nxt;
    edge() {}
    edge(const int _to, const int _cap, const int _nxt) {
        to = _to;
        cap = _cap;
        nxt = _nxt;
    }
}dat[MAXE<<1];

int head[MAXV], tail;

void add_edge(int from, int to, int cap) {
    dat[tail] = edge(to, cap, head[from]);
    head[from] = tail++;
    dat[tail] = edge(from, 0, head[to]);
    head[to] = tail++;
}

int level[MAXV], iter[MAXV];

queue<int> que;
void bfs(int s, int t) {    
    memset(level, 0xff, sizeof(int) * (t + 1));
    level[s] = 1;
    que.push(s);
    while(!que.empty()) {
        int u = que.front(); que.pop();
        for(int v = head[u]; ~v; v = dat[v].nxt) {
            edge &e = dat[v];
            if(level[e.to] < 0 && e.cap > 0) {
                level[e.to] = level[u] + 1;
                que.push(e.to);
            }
        }
    }
}

int dfs(int u, int t, int f) {
    if(u == t) return f;
    for(int &v = iter[u]; ~v; v = dat[v].nxt) {
        edge &e = dat[v];
        if(e.cap > 0 && level[e.to] > level[u]) {
            int d = dfs(e.to, t, min(f, e.cap));
            if(d > 0) {
                e.cap -= d;
                dat[v^1].cap += d;
                return d;
            }
        }
    }
    return 0;
}

int max_flow(int s, int t) {
    int res = 0, f;
    while(true) {
        bfs(s, t);
        if(level[t] < 0) return res;
        memcpy(iter, head, sizeof(int) * (t + 1));
        while((f = dfs(s, t, INF)) > 0) res += f;
    }
    return res;
}

int par[MAXV], ru[MAXV];
int norml(int x) {return (x - 1) % V + 1;}
void print_path(int s, int t) {
    for(int i = 0; i <= V; ++i) par[i] = i;
    for(int i = 1; i <= V; ++i) {
        for(int v = head[i]; ~v; v = dat[v].nxt) {
            if(dat[v].cap == 0 && dat[v].to != s && dat[v].to != t) {
                par[norml(i)] = norml(dat[v].to);
                ++ru[par[norml(i)]];
            }
        }
    }
    for(int i = 1; i <= V; ++i) if(!ru[i]) {
        for(int v = i; ; v = par[v]) {
            printf(par[v] == v ? "%d\n" : "%d ", v);
            if(v == par[v]) break;
        }
    }
}

int main()
{
    scanf("%d%d", &V, &E);
    int s = 0, t = V * 2 + 1;
    memset(head, 0xff, sizeof(int) * (t + 1));
    tail = 0;
    for(int i = 1; i <= V; ++i) {
        add_edge(s, i, 1);
        add_edge(i + V, t, 1);
    }
    for(int i = 0; i < E; ++i) {
        int x, y;
        scanf("%d%d", &x, &y);
        add_edge(x, y + V, 1);
    }
    int cnt = V - max_flow(s, t);
    print_path(s, t);
    printf("%d\n", cnt);
    return 0;
}

题目描述

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

设计一个有效算法求一个有向无环图 G 的最小路径覆盖。

输入格式

第 1 行有 2 个正整数 n 和 m。 n 是给定有向无环图 G 的顶点数,m 是 G 的边数。
接下来的 m 行,每行有 2 个正整数 u 和 v,表示一条有向边 (i,j) ( i , j )

输出格式

从第 1 1 1 行开始,每行输出一条路径。
文件的最后一行是最少路径数。

样例
样例输入
11 12
1 2
1 3
1 4
2 5
3 6
4 7
5 8
6 9
7 10
8 11
9 11
10 11
样例输出
1 4 7 10 11
2 5 8
3 6 9
3
数据范围与提示
1n200,1m6000 1 ≤ n ≤ 200 , 1 ≤ m ≤ 6000

你可能感兴趣的:(网络流)