zoukankan      html  css  js  c++  java
  • 网络流学习笔记

    网络流学习

    https://www.cnblogs.com/SYCstudio/p/7260613.html

    https://cn.vjudge.net/contest/282008#discuss

    https://www.cnblogs.com/LUO77/p/6115057.html

    最大流 Dinic

    由于基本上都是看着博客一步一步来的, 没有什么自己的想法, 但是不记录的话下次又没地方看, 所以稍微概括一下

    • 建图
      • 原图中一条有向边对应新图的这条边 + 边权为 (0) 的反边
      • 注意边从 (0) 开始标号, 目的为方便获取反边
    • 判断还有无增广路
      • 用 BFS 建立分层图 & 判断, 具体为若这条边有流, 且对面的点未分层, 则 lev[v] = lev[u] + 1
    • 计算增广的流量, 并更新网络
      • 用 DFS 递归来找流, 具体为 DFS(u, t, nowf) 表示当前点, 汇点, 当前上限, 走分层差1且有流的边, 到汇点直接返回值, 正反边更新
    • 不断循环上述两步, 将每次增广的流量加入答案, 知道无增广路
    // 搭配飞行员(二分图)
    int n, m, S, T;
    
    const int MAXN = 110, MAXM = MAXN * MAXN; // note x2
    
    int cur[MAXN], head[MAXN] ,nume;
    struct Adj { int nex, to; LL w; } adj[MAXM << 1];
    void addedge(int from, int to, LL w)
    {
        adj[nume] = (Adj) { head[from], to, w } ;
        head[from] = nume ++;
    }
    
    namespace Dinic
    {
        void link(int u, int v, LL w)
        {
    	addedge(u, v, w);
    	addedge(v, u, 0);
        }
        const LL INF = 1e10;
        int lev[MAXN];
        queue <int> q;
        bool BFS(int s, int t)
        {
    	memset(lev, 0, sizeof lev);
    	while (!q.empty()) q.pop();
    	q.push(s);
    	lev[s] = 1;
    	while (!q.empty())
    	{
    	    int u = q.front(); q.pop();
    	    for (int i = head[u]; i != -1; i = adj[i].nex)
    	    {
    		int v = adj[i].to;
    		if (lev[v] || adj[i].w == 0) continue;
    		lev[v] = lev[u] + 1;
    		q.push(v);
    	    }
    	}
    	return (lev[t] != 0);
        }
        LL DFS(int u, int t, LL nowf)
        {
    	if (u == t) return nowf;
    	LL sum = 0;
    	for (int & i = cur[u]; i != -1; i = adj[i].nex)
    	{
    	    LL v = adj[i].to;
    	    if (lev[v] != lev[u] + 1 || adj[i].w == 0) continue;
    	    int nowv = DFS(v, t, min(adj[i].w, nowf - sum));
    	    sum += nowv;
    	    adj[i].w -= nowv;
    	    adj[i ^ 1].w += nowv;
    	    if (sum == nowf) break;
    	}
    	return sum;
        }
        LL Dinic(int s, int t)
        {
    	LL ret = 0;
    	while (BFS(s, t))
    	{
    	    for (int i = 0; i <= n + 1; ++ i) cur[i] = head[i];
    	    LL nowflow = DFS(s, t, INF);
    	    ret += nowflow;
    	}
    	return ret;
        }
    }
    void build()
    {
        S = 0, T = n + 1;
        for (int i = 1; i <= m; ++ i) Dinic::link(S, i, 1);
        for (int i= m + 1; i <= n; ++ i) Dinic::link(i, T, 1);
        int a, b;
        while (scanf("%d%d", &a, &b) != EOF && a != -1)
        {
    	Dinic::link(a, b, 1);
        }
    }
    
    int main()
    {
        m = in();
        n = in(); 
        memset(head, -1, sizeof head);
        build();
        int ans = Dinic::Dinic(S, T);
        if (ans == 0) return puts("No Solution!"), 0;
        printf("%d
    ", ans);
        for (int u = 1; u <= m; ++ u)
    	for (int i = head[u]; i != -1; i = adj[i].nex)
    	    if (adj[i].to > m && adj[i].w == 0) printf("%d %d
    ", u, adj[i].to);
        return 0;
    }
    /*
    */
    
    

    https://blog.csdn.net/clove_unique/article/details/54884437

    搭配飞行员 <二分图 + 方案>

    题意: 如题

    Sol:

    源点汇点分别向两块连边, 然后题目中给定的连边, 边权都是 (1)
    跑最大流即可
    输出方案若两块间连边流掉了, 那么就一定选了这条边, 因为 Dinic 流程中都是更正为实际流量的, 不会存在错误的流量, 要不然最大流也算不对

    模板代码

    最小费用最大流 Dinic + SPFA

    https://www.cnblogs.com/fzl194/p/8859308.html

    还是概括...

    • 最大流是定值
    • 不管怎么增广, 增广到不行为止, 一定能得到最大流
    • 那么沿着(单价的和)最短路增广就可以满足最大流下最小费用
    int n, m, S, T;
    
    const int MAXN = 5e3 + 10, MAXM = 5e4 + 10; // note x2
    
    int head[MAXN] ,nume;
    struct Adj { int nex, to; LL w, c; } adj[MAXM << 1];
    void addedge(int from, int to, LL w, LL c)
    {
        adj[nume] = (Adj) { head[from], to, w, c } ;
        head[from] = nume ++;
    }
    
    typedef pair<LL, LL> PLL;
    #define mkpr make_pair
    #define fir first
    #define sec second
    
    namespace MCMF
    {
        void link(int u, int v, LL w, LL c)
        {
    	addedge(u, v, w, c);
    	addedge(v, u, 0, -c);
        }
        const LL INF = 1e10, INFD = 1e10;
        int pre[MAXN];
        LL dis[MAXN], f[MAXN];
        bool inq[MAXN];
        queue <int> q;
        bool SPFA(int s, int t)
        {
    	for (int i = 1; i <= n; ++ i) dis[i] = INFD, inq[i] = false;
    	while (!q.empty()) q.pop();
    	q.push(s); dis[s] = 0; inq[s] = true; f[s] = INF;
    	while (!q.empty())
    	{
    	    int u = q.front(); q.pop();
    	    for (int i = head[u]; i != -1; i = adj[i].nex)
    	    {
    		int v = adj[i].to; LL c = adj[i].c;
    		if (dis[u] + c < dis[v] && adj[i].w > 0)
    		{
    		    dis[v] = dis[u] + c;
    		    pre[v] = i ^ 1;
    		    f[v] = min(f[u], adj[i].w);
    		    if (!inq[v]) inq[v] = true, q.push(v);
    		}
    	    }
    	    inq[u] = false;
    	}
    	return (dis[t] != INFD);
        }
        PLL MCMF(int s, int t)
        {
    	LL flow = 0, cost = 0;
    	while (SPFA(s, t)) 
    	{
    	    flow += f[t];
    	    cost += f[t] * dis[t];
    	    int now = t;
    	    while (now != s)
    	    {
    		adj[pre[now] ^ 1].w -= f[t];
    		adj[pre[now]].w += f[t];
    		now = adj[pre[now]].to;
    	    }
    	}
    	return mkpr(flow, cost);
        }
    }
    void build()
    {
        S = in(); T = in();
        for (int i = 1; i <= m; ++ i)
        {
    	int u = in(), v = in(); LL w = in(), c = in();
    	MCMF::link(u, v, w, c);
        }
    }
    
    int main()
    {
        n = in(); m = in();
        memset(head, -1, sizeof head);
        build();
        PLL ans = MCMF::MCMF(S, T);
        printf("%lld %lld
    ", ans.fir, ans.sec);
        return 0;
    }
    
    

    最小路径覆盖

    要求选尽量少的不相交路径, 覆盖所有点

    Sol:
    本来每个点需要一条路径, 连接一对点, 即可减少一条路径,
    所以拆成出点入点, 二分图最大匹配
    输出方案注意一下

    上下界网络流

    无源汇可行流

    有源汇可行流

    有源汇最大流

    有源汇最小流

  • 相关阅读:
    Java:synchronized关键字引出的多种锁
    Java:Web Service初入门
    Java:HashMap原理与设计缘由
    Java:集合类的数据结构
    NoSQL数据库兴起
    Hadoop介绍与安装
    Java:泛型的理解
    《代码整洁之道》总结和笔记
    shell运算
    shell变量
  • 原文地址:https://www.cnblogs.com/Kuonji/p/11844106.html
Copyright © 2011-2022 走看看