• 网络流扩展知识


    网络流扩展知识

    最小费用最大流

    luogu P3381 【模板】最小费用最大流

    解析:

    1. 先用spfa求出最短路径(单位流量费用最少)
    2. 多路增广流掉这些流量(spfa没有记录dep信息,但凭借dis[]信息的关系不能保证不会访问之前访问过的节点,所以需要vis[]标记)
    code
    #include<bits/stdc++.h>
    #include<iostream>
    using namespace std;
    #define CL(a,b) memset(a,b,sizeof(a))
    #define db(x) cout<<"["<<#x<<"]="<<x<<endl
    #define fast() ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0)
    
    const int inf = 0x3f3f3f3f;
    const int maxn = 5e3+100;
    const int maxm = 5e4+100;
    
    struct edge{
        int u,v,cap,cost,nxt;
    }es[maxm*100];
    int cnt, head[maxn],dis[maxn],vis[maxn];
    void addEdge(int u,int v,int cap,int cost){
        es[cnt].u = u, es[cnt].v = v,es[cnt].cap = cap,es[cnt].cost=cost;
        es[cnt].nxt = head[u], head[u] = cnt, cnt++;
    }
    void addFlow(int u,int v,int cap,int cost){
        addEdge(u,v,cap,cost);
        addEdge(v,u,0,-cost);
    }
    int spfa(int s,int t){
        /*
        spfa: 利用队列中元素对最短路径进行更新,将能够更新且不在队列中的点加入队列
        */
        CL(dis,inf);
        CL(vis,0);
        queue<int> q;
        dis[s] = 0, vis[s] = 1;
        q.push(s);
        while(!q.empty()){
            int tmp = q.front(); q.pop();
            vis[tmp] = 0;
            for(int k = head[tmp];k!=-1;k = es[k].nxt){
                int u= es[k].u, v = es[k].v, cap = es[k].cap, cost = es[k].cost;
                if(dis[v]>dis[tmp]+cost&&cap>0){//cap>0表示联通,dis记录spfa的距离
                    dis[v] = dis[tmp]+cost;
                    if(!vis[v]) {vis[v] = 1; q.push(v);}
                }
            }
        }
        return dis[t]<inf;
    }
    int dinic(int s,int t,int fl,int &mincost){//mincost记录最小费用
        //本质是dfs,采用类dinic算法应该注意:
        // spfa没有提供层次信息,dis[v]==dis[u]+cost只意味着他们有着最短路径更新的关系,并不能保证不出现环(0)
        //图中k可能出现0环,所以需要vis[]防止访问了之前访问过的节点
        if(s==t||fl<=0) return fl;
        int res = 0;
        vis[s] = 1;
        for(int k = head[s];k!=-1;k = es[k].nxt){
            int u = es[k].u, v = es[k].v, cap = es[k].cap, cost = es[k].cost;
            if(!vis[v]&&cap>0&&dis[v] == dis[u]+cost){ //类似dinic多路增广
                int f = dinic(v,t,min(fl,cap),mincost);
                res+=f, es[k].cap-=f, es[k^1].cap+=f, fl-=f;
                mincost+=(f*cost);
                //db(res);
            }
        }
        vis[s] = 0;
        return res;
    }
    int n,m,s,t;
    int u,v,cap,cost;
    int main(){
        fast();
        scanf("%d %d %d %d",&n,&m,&s,&t);
        //cin>>n>>m>>s>>t;
        cnt = 0;
        CL(head,-1);
        for(int i=0;i<m;i++){
            scanf("%d %d %d %d",&u,&v,&cap,&cost);
            //cin>>u>>v>>cap>>cost;
            addFlow(u,v,cap,cost);
        }
        int ans = 0;
        int mincost = 0;
        //db("build graph");
        while(spfa(s,t)){
            ans+=dinic(s,t,inf,mincost);
            //db(ans);
            //db(mincost);
        }
        cout<<ans<<" "<<mincost<<endl;
    }
    

    二分图的多重匹配

    hiho 1393

    code
    #include<bits/stdc++.h>
    using namespace std;
    #define CL(a,b) memset(a,b,sizeof(a))
    #define db(x) cout<<"["<<#x<<"]="<<x<<endl
    #define fast() ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0)
    
    int t,n,m,cnt,need;
    const int maxn = 220;
    const int maxm = 110*110*2;
    const int inf = 0x3f3f3f3f;
    int head[maxn],dep[maxn],num[maxn];
    struct edge{
        int u,v,cap,nxt;
    }es[maxm];
    void addEdge(int u,int v, int cap){
        es[cnt].u = u, es[cnt].v= v, es[cnt].cap = cap;
        es[cnt].nxt = head[u], head[u] = cnt, cnt++;
    }
    void addFlow(int u,int v,int cap){
        addEdge(u,v,cap);addEdge(v,u,0);
    }
    void build_graph(){
        CL(head,-1); cnt = 0;need = 0;
        cin>>n>>m;
        int t,a,b;
        for(int i=1;i<=m;i++){cin>>t; need+=t;addFlow(n+i,n+m+1,t);}
        for(int i=1;i<=n;i++){
            cin>>a>>b;
            addFlow(0,i,a);
            for(int j=1;j<=b;j++){
                cin>>t; addFlow(i,t+n,1);
            }
        }
    }
    int bfs(int s,int t){
        CL(dep,-1); dep[s] = 0; queue<int> q; q.push(s);
        while(!q.empty()){
            int tmp = q.front(); q.pop();
            for(int k = head[tmp];k!=-1;k = es[k].nxt){
                int u = es[k].u, v = es[k].v, cap = es[k].cap;
                if(cap>0&&dep[v]<0){
                    dep[v] = dep[u]+1; q.push(v);
                }
    
            }
        }
        return dep[t]>-1;
    }
    int dinic(int s,int t,int fl){
        if(s==t||fl<=0) return fl;
        int res = 0;
        for(int k = head[s];k!=-1;k=es[k].nxt){
            int u = es[k].u, v = es[k].v, cap =es[k].cap;
            if(cap>0&&dep[v]==dep[u]+1){
                int f = dinic(v,t,min(fl,cap));
                fl-=f, res+=f, es[k].cap-=f, es[k^1].cap+=f;
            }
        }
        return res;
    }
    int main(){
        fast(); cin>>t;
        for(int i=1;i<=t;i++){
            build_graph();
            int ans = 0;
            while(bfs(0,n+m+1)){
                ans+=dinic(0,n+m+1,inf);
            }
            if(ans == need) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
            //cout<<ans<<endl;
        }
    
    }
    

    最小路径覆盖

    解析:
    利用前驱,后继建二部图,转换成对打匹配问题;后继没有被匹配上的点代表是某条路径的起点

    code
    
    #include<bits/stdc++.h>
    using namespace std;
    #define CL(a,b) memset(a,b,sizeof(a))
    #define db(x) cout<<"["<<#x<<"]="<<x<<endl
    #define fast() ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0)
    const int maxn = 1020;
    const int maxm = 3e4+510;
    const int inf = 0x3f3f3f3f;
    int n,m,dep[maxn],head[maxn],s,t,cnt;
    struct edge{
        int u,v,cap,nxt;
    }es[maxm<<1];
    
    void addEdge(int u,int v,int cap){
        es[cnt].u = u, es[cnt].v = v, es[cnt].cap = cap;
        es[cnt].nxt = head[u], head[u] = cnt, cnt++;
    }
    void addFlow(int u,int v,int cap){
        addEdge(u,v,cap); addEdge(v,u,0);
    }
    int bfs(int s,int t){
        CL(dep,-1); dep[s] = 0; queue<int> q; q.push(s);
        while(!q.empty()){
            int tmp = q.front(); q.pop();
            for(int k = head[tmp];k!=-1;k = es[k].nxt){
                int u = es[k].u, v = es[k].v, cap = es[k].cap;
                if(cap>0&&dep[v]<0){
                    dep[v] = dep[u]+1;
                    q.push(v);
                }
            }
        }
        return dep[t]>-1;
    }
    int dinic(int s,int t,int fl){
        if(s==t||fl<=0) return fl;
        int res = 0;
        for(int k = head[s];k!=-1;k = es[k].nxt){
            int u =es[k].u, v = es[k].v , cap = es[k].cap;
            if(cap>0&&dep[v]==dep[u]+1){
                int f = dinic(v,t,min(fl,cap));
                fl-=f,res+=f,es[k].cap-=f,es[k^1].cap+=f;
            }
        }
        return res;
    }
    
    int main(){
        fast();
        scanf("%d %d",&n,&m); cnt = 0;
        CL(head,-1);
        for(int i=1;i<=m;i++){
            int u,v; 
            scanf("%d %d",&u,&v);
            addFlow(u,v+n,1);
        }
        for(int i=1;i<=n;i++){
            addFlow(0,i,1);
            addFlow(i+n,2*n+1,1);
        }
        int ans = 0;
        while(bfs(0,2*n+1)){
            ans += dinic(0,2*n+1,inf);
            //db(ans);
        }
        printf("%d
    ",n-ans);
    
    
    }
    

    转换成最小割问题

    • 最大权闭合子图(s->正权点,负权点->t)
    • 最小点权值覆盖集
    • 最大点权独立集
  • 相关阅读:
    8.1.1 播放合成声音
    CSS 用伪类 :after 选择器清除浮动
    javascript 拖拽
    javascript 回到顶端
    用 console.time()和 console.timeEnd() 测试你的 javascript 代码执行效率
    【风马一族_代码英语】代码英语之五
    【风马一族_代码英语】代码英语之四
    【风马一族_代码英语】代码英语之三
    【风马一族_Android】第4章Android常用基本控件
    【风马一族_日常灵感】在美国得到的人生感悟
  • 原文地址:https://www.cnblogs.com/fridayfang/p/11960692.html
Copyright © 2020-2023  润新知