网络流之最大流(最小割)板子(Dinic 很快哦~~~) (还有玄学邻接表写法)

模板题HDU - 3549
最大流的精华在于反向边. (当然还是首选)
刘汝佳的(很快的, 一般的都能跑) (如果会T, 边的容量会爆int的, 小心的传inf即可, 能够就行)

int cnt ;
int n, m, s, t;
bool vis[maxn];
int d[maxn], cur[maxn];
struct Edge {
    int u, v;
    int cap, flow;
} e[maxn*6];    //因为是双向边  所以记得开二倍
vector<int> G[maxn];
void init() {
    cnt = 0;
    for (int i = 1 ; i <= n ; i ++) G[i].clear();
}

void add(int u, int v, int cap, int f) {
    e[cnt] = Edge{u, v, cap, f};
}

void AddEdge(int u, int v, int cap) {
    add(u, v, cap, 0);
    G[u].push_back(cnt++);
    add(v, u, 0, 0);
    G[v].push_back(cnt++);
}

bool BFS() {
    Fill(vis, 0);
    queue<int> q; q.push(s);
    vis[s] = 1; d[s] = 0;
    while (!q.empty()) {
        int v = q.front(); q.pop();
        for (int i = 0; i < G[v].size(); i++) {
            Edge &te = e[G[v][i]];
            if (!vis[te.v] && te.cap > te.flow) { //只考虑残量网络的弧
                vis[te.v] = 1;
                d[te.v] = d[v] + 1;
                q.push(te.v);
            }
        }
    }
    return vis[t];
}

int dfs(int x, int a) {
    if (x == t || a == 0) return a;
    int flow = 0, f;
    for (int &i = cur[x]; i < G[x].size(); i++) { //从上次考虑的弧
        Edge &te = e[G[x][i]];
        if (d[x] + 1 == d[te.v] && (f = dfs(te.v, min(a, te.cap - te.flow))) > 0) {
            te.flow += f;
            e[G[x][i]^1].flow -= f;
            flow += f;
            a -= f;
            if (a == 0) break;
        }
    }
    return flow;
}

int Dinic() {
    int flow = 0;
    while (BFS()) {
        Fill(cur, 0);
        flow += dfs(s, inf);
    }
    return flow;
}

void solve() {
    scanf("%d%d", &n, &m);
    init();
    for (int i = 1 ; i <= m ; i ++) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        AddEdge(u, v, w);
    }
    s = 1, t = n;
    printf("%d\n", Dinic());
}

优秀的邻接表写法 (玄学题)

const int maxn = 2e5+5;
struct node{
    int u, v, cap, next;
}e[maxn*10];
int n, m, s, t;
int cnt, head[maxn];
int d[maxn];
void init() {
    cnt =0 ; Fill(head, -1);
}

void add(int u, int v, int c) {
    e[cnt] = node{u, v, c, head[u]};
    head[u] = cnt++;
}

void AddEdge(int u, int v, int c) {
    add(u, v, c);
    add(v, u, 0);
}

int BFS() {
    queue <int> q; Fill(d, -1);
    d[s]=0; q.push(s);
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        for(int i = head[u] ; ~i ; i = e[i].next) {
            int to = e[i].v;
            if(d[to] == -1 && e[i].cap > 0) {
                d[to] = d[u]+1;
                q.push(to);
            }
        }
    }
    return d[t] != -1;
}

int dfs(int u, int a) {
    int r = 0;
    if(u == t) return a;
    for(int i = head[u] ; ~i && r < a ;i = e[i].next) {
        int to = e[i].v;
        if(e[i].cap > 0 && d[to] == d[u] + 1) {
            int x = min(e[i].cap, a-r);
            x = dfs(to, x);
            r += x;
            e[i].cap -= x; e[i^1].cap += x;
        }
    }
    if(!r) d[u]=-2;
    return r;
}

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

void solve() {
    scanf("%d%d", &n, &m);
    init(); s = 0, t = 2*n + 1;
    for (int i = 1 ; i <= m ; i ++) {
        int u, v;
        scanf("%d%d", &u, &v);
        AddEdge(u, v+n, inf);
    }
    for (int i = 1 ; i <= n ; i ++) {
        AddEdge(i+n, i, inf);
        AddEdge(s, i, 1);
        AddEdge(i+n, t, 1);
    }
    printf("%d\n", n - Dinic());
}

double的Dinic模板, 求最大密度子图( 注意比较大小都要有sgn! 不然各种错误!)
题目:华科决赛K题. 求n个数字中, 选择一些数字使得其中的pair数/这个数字集合size最大.
pair数定义为 (u, v) u % v == 0 || v % u == 0.

const int inf = 0x3f3f3f3f; //用这个可以直接mem
const ll INF = 1e18;
const int mod = 1e9+7;
const int maxn = 1e2+5;
const int maxm = 1e5 + 5;
const db eps = 1e-7;
const db eps2 = 1e-4;
int head[maxn], cnt;
int n, m, s, t;
bool vis[maxn], mark[maxn];
int d[maxn], cur[maxn], du[maxn];
struct Edge {
    int u, v;
    db cap, flow;
} e[maxm]; //因为是双向边  所以记得开二倍

vector<int> G[maxn];
int sgn(db x) {
    return x < -eps ? -1 : x < eps ? 0 : 1;
}

void init() {
    for (int i = 0; i < maxn; i++) G[i].clear();
    cnt = 0;
}

void add(int u, int v, db cap, db f) {
    e[cnt] = (Edge){u, v, cap, f};
}

void AddEdge(int u, int v, db cap) {
    add(u, v, cap, 0);
    G[u].push_back(cnt++);
    add(v, u, 0, 0);
    G[v].push_back(cnt++);
}

bool BFS() {
    memset(vis, 0, sizeof(vis));
    queue<int> q;
    q.push(s);
    vis[s] = 1;
    d[s] = 0;
    while (!q.empty()) {
        int v = q.front(); q.pop();
        for (int i = 0; i < G[v].size(); i++) {
            Edge &te = e[G[v][i]];
            if (!vis[te.v] && sgn(te.cap - te.flow) > 0) { //只考虑残量网络的弧
                vis[te.v] = 1;
                d[te.v] = d[v] + 1;
                q.push(te.v);
            }
        }
    }
    return vis[t];
}

db dfs(int x, db a) {
    if (x == t || sgn(a) == 0) return a;
    db flow = 0, f;
    for (int &i = cur[x]; i < G[x].size(); i++) { //从上次考虑的弧
        Edge &te = e[G[x][i]];
        if (d[x] + 1 == d[te.v] && sgn(f = dfs(te.v, min(a, te.cap - te.flow))) > 0) {
            te.flow += f;
            e[G[x][i]^1].flow -= f;
            flow += f;
            a -= f;
            if (sgn(a) == 0) break;
        }
    }
    return flow;
}

db Dinic() {
    db flow = 0;
    while (BFS()) {
        Fill(cur, 0);
        flow += dfs(s, inf);
    }
    return flow;
}

struct node {
    int u, v;
}e2[maxn*10];

bool check(db g) {
    init();
    for (int i = 1 ; i <= n ; i ++) {
        AddEdge(s, i, m);
        AddEdge(i, t, m+2*g-du[i]);
    }
    for (int i = 1 ; i <= m ; i ++) {
        AddEdge(e2[i].u, e2[i].v, 1);
        AddEdge(e2[i].v, e2[i].u, 1);
    }
    return sgn((1.0*n*m - Dinic()));
}

int a[maxn];
void solve() {
    while(~scanf("%d", &n)) {
        Fill(du, 0); s = 0, t = n + 1;
        for (int i = 1 ; i <= n ; i ++) {
            scanf("%d", a+i);
        }
        m = 0;
        for (int i = 1 ; i <= n ; i ++) {
            for (int j = i + 1 ; j <= n ; j ++) {
                if (a[j] % a[i] == 0 || a[i] % a[j] == 0) {
                    ++ du[i]; ++du[j];
                    e2[++m] = node{i, j};
                }
            }
        }
        db l = 0, r = n, mid, ans = 0;
        while(sgn(l - r) < 0) {
            mid = (l + r) / 2;
            if (check(mid)) {
                ans = mid;
                l = mid;
            }
            else r = mid;
        }
        printf("%.8f\n", ans);
    }
}

网络流的一些经典模型:

1: 最大权闭合子图

描述:

给定一个点带权的有向图,求这个图的最大权闭合图?
* 闭合图含义:图中任意的出边所指的点都在图中的点集内。

解法:

在原图点集的基础上增加源 和汇 s t ;将原图每条有向边的容量替换为INF,
然后s连所有权值为正的点,边的权值为点的权值,所有权值为负的点连t,权值为点权值的绝对值,
最后所有权值为正的和减去最小割即为所求

2: 最大密度子图

描述:

给定一个无向图,要求它的一个子图,使得子图中边数 |E| 与点数 |V| 的比值最大,即最大化:

|E||V| | E | | V |

解法:

只能说解法不好说, 详细参考胡伯涛《最小割模型在信息学竞赛中的应用》, 只能分析出是求这个问题, 和具体的建图方法即可, 剩下的就是套板子了(逃.

3:二分图的最小点权覆盖:

描述:

最小点覆盖指的是选择尽量少的点, 使得每条边至少有一个端点被选中. 那么在二分图匹配中很容易可以被证明就是该个二分图的最匹配数. 最小点权覆盖就是选择的这些点的点权和要尽量的小

解法:

将原图的边权赋为INF,然后增加超级源汇点,S连左半图,左半图连T,权值为点上的值, 跑最大流即可

4:二分图的最大点权独立集:

描述:

最大独立集, 即选择尽量多的结点, 使得任意两个节点不相邻(即任意一条边的两个端点不会同时被选中), 最大独立集和最小点覆盖是互补的. 因此可以得出答案. 至于为什么是互补的, 就请好好想想. (白书P356)
并且这些点权和要尽量的大

解法:

所有节点的值减去二分图的最小点权覆盖

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