网络流扩展知识

网络流扩展知识

最小费用最大流

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 ```cpp #include using namespace std; #define CL(a,b) memset(a,b,sizeof(a)) #define db(x) cout<<"["<<#x<<"]="< 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",n-ans); } ```

转换成最小割问题

  • 最大权闭合子图(s->正权点,负权点->t)
  • 最小点权值覆盖集
  • 最大点权独立集

猜你喜欢

转载自www.cnblogs.com/fridayfang/p/11960692.html