zoukankan      html  css  js  c++  java
  • [最大流&费用流&上下界&最小流]-模板

    网络流

    (_{_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}}^{_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}}{_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}}_{_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}}^{_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}})

    1.最大流

    /***********************************
    dinic最大流思路:
        不断图分层,保证dfs过程中,不会出现环等现象
        在一次分层中,用dfs一次性搜寻多条增广路。
        就是向下搜,搜到t为止,然后返回流量。
        当前节点最大流量为的下属分支所有流量的返回最大流量。
        下属分支返回的流量为min(通往该分支边的最大流量,分支的下属总流量)
        当dfs一次后,一定会有某些边被榨干,改变分层图。
        再次分层图,重复上述过程。
        分层图用bfs实现。如果bfs不到终点,则结束算法。
    ************************************/
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<queue>
    using namespace std;
    typedef long long ll;
    typedef long long ii;
    const ll maxn = 205;
    const ll maxm = 2e3+40;
    const ll INF = ll(1)<<50;
    struct e{
    	ll next,to,flow;
    }edge[maxm*2];
    int head[maxn],tot=0;
    void add(ll u,ll v,ll flow){
    	edge[tot].to=v;edge[tot].next=head[u];
    	edge[tot].flow=flow;head[u]=tot++;
    	edge[tot].to=u;edge[tot].next=head[v];
    	edge[tot].flow=0;head[v]=tot++;
    }
    ll n,m,s,t;
    
    ll dis[maxn];
    queue<ll> q;
    ll cur[maxn];
    
    ll dfs(ll u,ll lim){
    	//cout<<"dfs"<<endl;
    //	/system("pause");
    	if(u==t)return lim;
    	ll flow=0;
    	for(ll i=cur[u];~i;i=edge[i].next){
    		cur[u]=i;
    		//最优弧优化。
    		//因为不是树,是网络图,所以回溯后,可能从其他分支再经过当前点。
    		//而当前点的之前的边流量一定已经被榨干了。
    		//所以不用考虑当前边的前边的边
    		ll v= edge[i].to;
    		if(dis[v]!=dis[u]+1||edge[i].flow==0){
                continue;
    		}
    		ll f=dfs(v,min(edge[i].flow,lim));
    		flow+=f;lim-=f;
    		edge[i].flow-=f;edge[i^1].flow+=f;
    		if(lim==0)break;
    	}
    	return flow;
    }
    
    ll bfs(){
    	while(!q.empty())q.pop();
    	for(ll i=1;i<=n;i++){
    		dis[i]=INF;
    		cur[i]=head[i];
    	}
    	dis[s]=0;
    	q.push(s);
    	while(!q.empty()){
    		ll u=q.front();q.pop();
    		for(ll i=head[u];~i;i=edge[i].next){
    			ll v= edge[i].to;
                //cout<<i<<" "<<v<<endl;
    			if(edge[i].flow==0||dis[v]!=INF)continue;
    			dis[v]=dis[u]+1;
    			q.push(v);
    		}
    	}
    	if(dis[t]==INF)return 0;
    	else return 1;
    }
    
    void init(){
    	for(ll i=0;i<=n;i++){
    		head[i]=-1;
    	}
    }
    int main(){
       // cout<<INF+1<<endl;
    	ll x;
    	scanf("%lld%lld%lld",&n,&m,&x);
    	init();
    	s=1;t=n;
    	ll a,b,c;
    	for(int i=0;i<m;i++){
    		scanf("%lld%lld%lld",&a,&b,&c);
    		add(a,b,c);
    	}
    	/*
    	for(int i=0;i<tot;i++){
            cout<<i<<":"<<edge[i].to<<" "<<edge[i].next<<" "<<edge[i].flow<<endl;
    	}*/
    	ll maxflow=0;
    	while(bfs()){
            maxflow+=dfs(s,INF);
            //cout<<maxflow<<endl;
    	}
        if(maxflow==0)puts("Orz Ni Jinan Saint Cow!");
    	else printf("%lld %lld",maxflow,(x+maxflow-1)/maxflow);
    	return 0;
    }
    
    

    2.最小费用流

    /******************************************************
    整体思路:
        首先在图中找到一条流不为0的最少费用路
        可用dij或者spfa,dij的话要给所有的加势;
        在找最短路的过程中,cost看做路径长度。
        用pre[i]来存储前一条边是哪条边,因为最后
        要处理所有途径边的流量,和计算cost;
        cost为dis[t]*incf[t];
        dis[t]为路径上累计长度,即累计单位流量花费。
        然后重复调用上述过程,直到找不到一条最短路径。
    *******************************************************/
    
    
    
    
    
    
    #include<iostream>
    #include<queue>
    #include<cstdlib>
    #include<cstdio>
    using namespace std;
    int n,m,s,t;
    int maxflow,mincost;
    const int maxn=5e3+50,maxm=5e4+50;
    
    struct e{
        int next,to,flow,cost;
    }edge[maxm*2];
    int head[maxn],tot=0;
    void add(int u,int v,int flow,int cost){
        edge[tot].flow=flow;edge[tot].cost=cost;
        edge[tot].next=head[u];edge[tot].to=v;
        head[u]=tot++;
        edge[tot].flow=0;edge[tot].cost=-cost;
        edge[tot].next=head[v];edge[tot].to=u;
        head[v]=tot++;
    }
    
    int dis[maxn];
    int incf[maxn];
    bool inq[maxn];
    int pre[maxn];
    bool spfa(){
        queue<int> q;
        for(int i=0;i<=n;i++){
            dis[i]=0x7fffffff;
            inq[i]=false;
        }
        q.push(s);
        inq[s]=1;dis[s]=0;
        incf[s]=0x7fffffff;
        while(!q.empty()){
            int u=q.front();q.pop();
            inq[u]=0;
            for(int i=head[u];~i;i=edge[i].next){
                if(edge[i].flow==0)continue;
                int v=edge[i].to;
                if(dis[v]>dis[u]+edge[i].cost){
                    dis[v]=dis[u]+edge[i].cost;
                    incf[v]=min(edge[i].flow,incf[u]);
                    pre[v]=i;
                    //存前驱的边是因为最后要通过前驱来快速会找到边从而操作cost和flow
                    //存点的遍历所有的边。
                    //前驱点的寻找可以用反向边的to来寻找。
                    if(!inq[v]){
                        q.push(v);inq[v]=true;
                    }
                }
            }
        }
        if(dis[t]==0x7fffffff)return 0;
        else return 1;
    }
    void MCMF(){
        while(spfa()){
            int x=t;
            maxflow += incf[t];
            mincost += dis[t]*incf[t];
            int i;
            while(x!=s){
                i=pre[x];
                edge[i].flow -= incf[t];
                edge[i^1].flow += incf[t];
                x = edge[i^1].to;
            }
        }
    }
    void init(){
        for(int i=0;i<=n;i++){
            head[i]=-1;
        }
    }
    
    int main(){
        maxflow=mincost=0;
        scanf("%d%d%d%d",&n,&m,&s,&t);
        init();
        int u,v,cost,flow;
        for(int i=0;i<m;i++){
            scanf("%d%d%d%d",&u,&v,&flow,&cost);
            add(u,v,flow,cost);
        }
        MCMF();
        printf("%d %d
    ",maxflow,mincost);
        return 0;
    }
    
    

    3.无源汇上下界可行流

    /*******************
    无源汇上下界可行流:
        首先将图中的下界流跑满
        建图的边改为上界减下界
        那么会出现部分点的流不平衡
        我们给这些点分别连一个起点和终点。
        给缺少出流的连上起点,缺少入流的连上终点。
        边的流上界为所缺少或多余的流
        这样在跑满最大流后,
        如果连接终点和起点的流跑满之后,
        把终点和起点去掉,那么终点和起点连接的节点
        会分别多出/少了连边对应边的满流。
        因为前面是按照缺少/多余建图
        所以缺少/多余各自补平。
    
        做法:
            建图,边为上界减下界,存下下界
            建源点汇点,连统计入度出度的边
            跑最大流
            输出边时输出下界流+最大流的流
    ********************/
    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <cmath>
    #include <cstdlib>
    #include <algorithm>
    using namespace std;
    const int maxn = 219;
    const int maxm = 10250;
    
    struct e {
        int next, to, flow;
    } edge[maxm * 2];
    int head[maxn], tot = 0;
    void add(int u, int v, int flow) {
        edge[tot].next = head[u];
        edge[tot].to = v;
        edge[tot].flow = flow;
        head[u] = tot++;
    
        edge[tot].next = head[v];
        edge[tot].to = u;
        edge[tot].flow = 0;
        head[v] = tot++;
    }
    
    int s, t, n, m;
    
    int cur[maxn + 2];
    int dis[maxn + 2];
    int dfs(int u, int lim) {
        if (u == t || lim == 0)
            return lim;
        int flow = 0;
        for (int i = cur[u]; ~i; i = edge[i].next) {
            // cout<<cur[u]<<endl;
            //  system("pause");
    
            cur[u] = i;
            int v = edge[i].to;
            if (dis[v] != dis[u] + 1)
                continue;
            int f = dfs(v, min(lim, edge[i].flow));
            flow += f;
            lim -= f;
            edge[i].flow -= f;
            edge[i ^ 1].flow += f;
            if (lim == flow)
                break;
        }
        return flow;
    }
    
    bool bfs() {
        for (int i = 0; i <= n + 5; i++) {
            dis[i] = 0x7fffffff;
            cur[i] = head[i];
        }
        dis[s] = 0;
        queue<int> q;
        q.push(s);
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int i = head[u]; ~i; i = edge[i].next) {
                int v = edge[i].to;
                if (dis[v] != 0x7fffffff || edge[i].flow == 0)
                    continue;
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
        if (dis[t] == 0x7fffffff)
            return 0;
        else
            return 1;
    }
    
    int maxflow = 0;
    bool dinic() {
        while (bfs()) {
            maxflow += dfs(s, 0x7fffffff);
        }
        for (int i = head[s]; ~i; i = edge[i].next) {
            if (edge[i].flow != 0)
                return 0;
        }
        return 1;
    }
    
    int level[maxn];
    int addd[maxm];
    void init() {
        for (int i = 0; i <= n + 2; i++) {
            level[i] = 0;
            head[i] = -1;
        }
        for (int i = 0; i < m; i++) {
            addd[i] = 0;
        }
    }
    
    int main() {
        freopen("2.in","r",stdin);
        scanf("%d%d", &n, &m);
        init();
        int a, b, l, u;
        for (int i = 0; i < m; i++) {
            scanf("%d%d%d%d", &a, &b, &l, &u);
            add(a, b, u - l);
            level[b] += l;
            level[a] -= l;
            addd[i] += l;
        }
        s = n + 1, t = n + 2;
        int first = 0;
        for (int i = 1; i <= n; i++) {
            if (level[i] > 0) {
                add(s, i, level[i]);
            }
            if (level[i] < 0) {
                first += level[i];
                add(i, t, -level[i]);
            }
        }
    
        if (dinic()) {
            puts("YES");
            for (int i = 0; i < m; i++) {
                cout << edge[i * 2 + 1].flow + addd[i] << endl;
            }
        } else
            puts("NO");
    
        return 0;
    }
    
    

    4.有源汇上下界最小流

    /*******************
    无源汇上下界可行流:
        首先将图中的下界流跑满
        建图的边改为上界减下界
        那么会出现部分点的流不平衡
        我们给这些点分别连一个起点和终点。
        给缺少出流的连上起点,缺少入流的连上终点。
        边的流上界为所缺少或多余的流
        这样在跑满最大流后,
        如果连接终点和起点的流跑满之后,
        把终点和起点去掉,那么终点和起点连接的节点
        会分别多出/少了连边对应边的满流。
        因为前面是按照缺少/多余建图
        所以缺少/多余各自补平。
    
        做法:
            建图,边为上界减下界,存下下界
            建源点汇点,连统计入度出度的边
            跑最大流
            输出边时输出下界流+最大流的流
    ********************/
    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <cmath>
    #include <cstdlib>
    #include <algorithm>
    using namespace std;
    const int maxn = 219;
    const int maxm = 10250;
    
    struct e {
        int next, to, flow;
    } edge[maxm * 2];
    int head[maxn], tot = 0;
    void add(int u, int v, int flow) {
        edge[tot].next = head[u];
        edge[tot].to = v;
        edge[tot].flow = flow;
        head[u] = tot++;
    
        edge[tot].next = head[v];
        edge[tot].to = u;
        edge[tot].flow = 0;
        head[v] = tot++;
    }
    
    int s, t, n, m;
    
    int cur[maxn + 2];
    int dis[maxn + 2];
    int dfs(int u, int lim) {
        if (u == t || lim == 0)
            return lim;
        int flow = 0;
        for (int i = cur[u]; ~i; i = edge[i].next) {
            // cout<<cur[u]<<endl;
            //  system("pause");
    
            cur[u] = i;
            int v = edge[i].to;
            if (dis[v] != dis[u] + 1)
                continue;
            int f = dfs(v, min(lim, edge[i].flow));
            flow += f;
            lim -= f;
            edge[i].flow -= f;
            edge[i ^ 1].flow += f;
            if (lim == flow)
                break;
        }
        return flow;
    }
    
    bool bfs() {
        for (int i = 0; i <= n + 5; i++) {
            dis[i] = 0x7fffffff;
            cur[i] = head[i];
        }
        dis[s] = 0;
        queue<int> q;
        q.push(s);
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int i = head[u]; ~i; i = edge[i].next) {
                int v = edge[i].to;
                if (dis[v] != 0x7fffffff || edge[i].flow == 0)
                    continue;
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
        if (dis[t] == 0x7fffffff)
            return 0;
        else
            return 1;
    }
    
    int maxflow = 0;
    bool dinic() {
        while (bfs()) {
            maxflow += dfs(s, 0x7fffffff);
        }
        for (int i = head[s]; ~i; i = edge[i].next) {
            if (edge[i].flow != 0)
                return 0;
        }
        return 1;
    }
    
    int level[maxn];
    int addd[maxm];
    void init() {
        for (int i = 0; i <= n + 2; i++) {
            level[i] = 0;
            head[i] = -1;
        }
        for (int i = 0; i < m; i++) {
            addd[i] = 0;
        }
    }
    
    int main() {
        freopen("2.in","r",stdin);
        scanf("%d%d", &n, &m);
        init();
        int a, b, l, u;
        for (int i = 0; i < m; i++) {
            scanf("%d%d%d%d", &a, &b, &l, &u);
            add(a, b, u - l);
            level[b] += l;
            level[a] -= l;
            addd[i] += l;
        }
        s = n + 1, t = n + 2;
        int first = 0;
        for (int i = 1; i <= n; i++) {
            if (level[i] > 0) {
                add(s, i, level[i]);
            }
            if (level[i] < 0) {
                first += level[i];
                add(i, t, -level[i]);
            }
        }
    
        if (dinic()) {
            puts("YES");
            for (int i = 0; i < m; i++) {
                cout << edge[i * 2 + 1].flow + addd[i] << endl;
            }
        } else
            puts("NO");
    
        return 0;
    }
    
    
  • 相关阅读:
    第五周学习总结
    第四周学习总结
    实验三报告
    第2,3周学习总结
    第二次实验报告
    实验一报告
    MyFirstStruts2
    java.lang.NoClassDefFoundError: org/apache/commons/lang/StringUtils
    Tomcat version 6.0 only supports J2EE 1.2, 1.3, 1.4,and Java EE 5 Web modules
    The JRE could not be found.Edit the server and change the JRE location.
  • 原文地址:https://www.cnblogs.com/greenpepper/p/13361886.html
Copyright © 2011-2022 走看看