网络流专项

dinic  网络流算法 :

 网络流数组要开大一点

反悔边的意义:当一条流量被流入,反向流入的时候,就相当于没有流过。

分层图的意义:对于给定的点,流入< =dep[ u ] 是没有意义的,不会使得当前流更优。

当前弧优化:当前的边已经流光,不再流入。

炸点优化:当前点已经流光,设dep为-1,不再流入。

多路增广:如果一个点可以多条流出去,一次dfs多条路径。

 BFS :每次建立一个分层图。

DFS:流过一条流

最大流=最小割

P3376 【模板】网络最大流

模板。

View Code

1001: [BeiJing2006]狼抓兔子

一个1e6的图,求最小割,数组开小了,TLE

View Code

网络流24题:

P2756 飞行员配对方案问题

简单二分图匹配,

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=1e3+5;
int gp[N][N],match[N],vis[N];
int n,m;
bool findpath(int u){
    for(int i=m+1;i<=n+m;i++){
        if(gp[u][i]&&!vis[i]){
            vis[i]=1;
            if(!match[i]||findpath(match[i])){
                match[i]=u;
                return 1;
            }
        }
    }
    return 0;
}   
void init(){
    memset(gp,0,sizeof gp);
    memset(match,0,sizeof match);
}
int main(){
    init();
    scanf("%d %d",&m,&n);
    int u,v;
    while(~scanf("%d %d",&u,&v)){
        if(u+v<0)break;
        gp[u][v]=1;
    }
    vector<int>ans;
    for(int i=1;i<=m;i++){
    
        memset(vis,0,sizeof vis);
        if(findpath(i))ans.pb(i);
    
    }
    for(int i=m+1;i<=n+m;i++){
        if(match[i])match[ match[i] ]=i;
    }
    cout<<ans.size()<<endl;
    for(int i=0;i<ans.size();i++){
        printf("%d %d
",ans[i],match[ ans[i] ]);
    }

    // system("pause");
    return 0;
}
View Code

P4015 运输问题

 将源点到每个仓库连一条费用为0,流量为 a [ i ]  的边,仓库与店铺连一条边,最后所有店铺与汇点连一条费用为0,流量为b [ i ] 的边;

跑一遍费用流即可。然后最大费用就是把费用全部变负,跑一遍费用流,然后最后结果取负即可。

但数组开小了,汇点越界,

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+50;
const int inf=0x3f3f3f3f;
int a[200],b[200],c[200][200];
int n,m,S,T;
int mincost;
struct Dinic{
    int head[N],dis[N],pre[N],flow[N],last[N];
    bool inq[N];
    int ecnt; 
    struct edge{
        int v,w,flow,next;
    }e[N*10];
    void init(){
        memset(head,-1,sizeof head);
        ecnt = 0;
    }
    void addedge(int u,int v,int w,int flow){
    e[ecnt].v=v;e[ecnt].w=w;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++;
    }

    bool spfa(){
        memset(dis,inf,sizeof dis);
        memset(flow,inf,sizeof flow);
        memset(inq,0,sizeof inq);
        queue<int>Q;
        Q.push(S);inq[S]=1;dis[S]=0;pre[T]=-1;
        while(!Q.empty()){
            int u=Q.front();Q.pop();inq[u]=0;
            for(int i=head[u];~i;i=e[i].next){
                int v=e[i].v,w=e[i].w;
                if(e[i].flow>0&&dis[v]>dis[u]+w){
                    dis[v]=dis[u]+w;pre[v]=u;last[v]=i;
                    flow[v]=min(flow[u],e[i].flow);
                    if(!inq[v]){
                    Q.push(v);inq[v]=1;
                    }
                }
            }
        }
        return pre[T]!=-1;
    }
    void  MCMF(){
        mincost=0;
        while(spfa()){
            int cur=T;
            mincost+=flow[T]*dis[T];
            while(cur!=S){
                e[last[cur]].flow-=flow[T];
                e[last[cur]^1].flow+=flow[T];
                cur=pre[cur];
            }
        }
    }

}dinic;  

void init_add(int t){
    for(int i=1;i<=m;i++){
        dinic.addedge(S,i,0,a[i]);
        dinic.addedge(i,S,0,0);
    }
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            dinic.addedge(i,j+m,t*c[i][j],b[j]);
            dinic.addedge(j+m,i,-c[i][j]*t,0);
        }
    }
    for(int i=1;i<=n;i++){
        dinic.addedge(i+m,T,0,b[i]);
        dinic.addedge(T,i+m,0,0);
    }
}
int main(){
    scanf("%d %d",&m,&n);
    S=0,T=10005;
    for(int i=1;i<=m;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)scanf("%d",&b[i]);
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                scanf("%d",&c[i][j]);
            }
        }

        dinic.init();
        init_add(1);
        dinic.MCMF();
        printf("%d
",mincost);

        dinic.init();
        init_add(-1);
        dinic.MCMF();

        printf("%d
",-mincost);
        // system("pause");
    return 0;
}
View Code

C - C

 CodeForces - 653D 

题意:一张图,找出p条路径,每条路径的流均为x,求最大的p*x;

考虑直接流就是每一条流增广流量为x,但这样不好check;

于是考虑如下做法,把每一条流 拆成 f / x , 最后判断最大流流是否大于p即可。

二分精度注意,小了大了都会爆。

#include<bits/stdc++.h>
using namespace std;
typedef double db;
const db eps=1e-6;
const int inf=0x3f3f3f3f;
const int N=1e4+5;
int n,m,S,T;
struct Dinic{
    int head[N],dep[N];
    int ecnt; 
    struct edge{
        int v,flow,next;
    }e[N*10];

    void init(){
        memset(head, -1, sizeof head);
        ecnt = 0;
    }
    void addedge(int u, int v, int  flow) {
    e[ecnt].v=v;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++;
    }

    bool BFS(){//分层图找增广路
        memset(dep,0,sizeof dep);
        queue<int>Q;
        Q.push(S);dep[S]=1;
        while(!Q.empty()){
            int u=Q.front();Q.pop();
            for(int i=head[u];~i;i=e[i].next){
                if(e[i].flow&&!dep[e[i].v]){
                    dep[e[i].v]=dep[u]+1;
                    Q.push(e[i].v);
                }
            }
        }
        return dep[T];
    }

    int DFS(int u, int f){//推进新流
        if(u==T||f==0)return f;
        int w,used=0;
        for(int i=head[u];~i;i=e[i].next){
            if(e[i].flow&&dep[e[i].v]==dep[u]+1){
                w=DFS(e[i].v,min(f,e[i].flow));//多路增广
                e[i].flow-=w;e[i^1].flow+=w;
                used+=w;f-=w;
            }
        }
        if(!used)dep[u]=-1;//炸点优化
        return used;
    }
    int maxflow() {
        int ans=0;
        while(BFS()){
            ans+=DFS(S,inf);
        }
        return ans;
    }
}dinic; 
int p;
struct node{int x,y;db z;}G[N];
bool check(db k){
    dinic.init();
    for(int i=1;i<=m;i++){
        int tmp=min(p*1.0,G[i].z/k);
        dinic.addedge(G[i].x,G[i].y,tmp);
        dinic.addedge(G[i].y,G[i].x,0);
    }
    int cur=dinic.maxflow();
    // printf("k = %lf cur = %d
",k,cur);
    return cur>=p;
}
int main(){
    scanf("%d %d %d",&n,&m,&p);
    S=1,T=n;
    for(int i=1;i<=m;i++){
        int u,v;db f;
        scanf("%d %d %lf",&G[i].x,&G[i].y,&G[i].z);
    }

    db mid,l=0,r=1e12;
    for(int i=1;i<=100;i++){
        mid=(r+l)/2;
        if(check(mid))l=mid;
        else r=mid;
        // printf("%.6lf
",mid);
    }

    printf("%.6lf
",l*p);
    // system("pause");
    return 0;
}
View Code

Farm Tour

 POJ - 2135 

 题意:从1走到n,再从n走到1,不能重复走一条边,求最短路。

费用流做法:源点到1连一条流量2,费用0的边,汇点到n连一条流量为2,费用为0的边,跑一遍费用流。

#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
typedef double db;
const db eps=1e-6;
const int inf=0x3f3f3f3f;
const int N=1e4+5;
int n,m,S,T,mincost;
struct Dinic{
    int head[N],dep[N],dis[N],pre[N],flow[N],last[N];
    //dep记录分层图,pre点前驱,flow点的流,last点的前一条边,dis表示费用
    bool inq[N];
    int ecnt; 
    struct edge{
        int v,w,flow,next;
    }e[N*10];
    void init(){
        memset(head, -1, sizeof head);
        ecnt = 0;
    }
    void addedge(int u,int v,int w,int flow){
    e[ecnt].v=v;e[ecnt].w=w;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++;
    }
    bool spfa(){//spfa找增广路
        memset(dis,inf,sizeof dis);
        memset(flow,inf,sizeof flow);
        memset(inq,0,sizeof inq);
        queue<int>Q;
        Q.push(S);inq[S]=1;dis[S]=0;pre[T]=-1;
        while(!Q.empty()){
            int u=Q.front();Q.pop();inq[u]=0;
            for(int i=head[u];~i;i=e[i].next){
                int v=e[i].v,w=e[i].w;
                if(e[i].flow>0&&dis[v]>dis[u]+w){
                    dis[v]=dis[u]+w;pre[v]=u;last[v]=i;
                    flow[v]=min(flow[u],e[i].flow);
                    if(!inq[v]){
                    Q.push(v);inq[v]=1;
                    }
                }
            }
        }
        return pre[T]!=-1;
    }
    void  MCMF(){
     mincost=0;
        while(spfa()){//推进新流
            int cur=T;
            // maxflow+=flow[T];
            mincost+=flow[T]*dis[T];
            while(cur!=S){//汇点向前回溯
                e[last[cur]].flow-=flow[T];
                e[last[cur]^1].flow+=flow[T];
                cur=pre[cur];
            }
        }

    }
}dinic;  
int main(){
    scanf("%d %d",&n,&m);
    dinic.init();
    for(int i=1,u,v,w;i<=m;i++){
        scanf("%d %d %d",&u,&v,&w);
        dinic.addedge(u,v,w,1);
        dinic.addedge(v,u,-w,0);
        dinic.addedge(v,u,w,1);
        dinic.addedge(u,v,-w,0);
    }
    S=0,T=n+1;
    dinic.addedge(S,1,0,2);
    dinic.addedge(1,S,0,0);
    dinic.addedge(n,T,0,2);
    dinic.addedge(T,n,0,0); 
    dinic.MCMF();
    printf("%d
",mincost);

    // system("pause");
    return 0;
}
View Code

T123574 Voluntary Hotel

毒题;

题意:一个无向带权图里,r个医生,p个医院,q个宾馆,每个医生对应着一个医院,每个宾馆有一个容量h[ i ] ,

可以从家到医院,或者从宾馆到医院,求所有医生去上班的最大路径最小。

解法:考虑最大值最小化,二分一个d,判断在距离 d 内,所有的医生是否能走到对应的医院,

先预处理每一个点到医院的距离。

如果医生家到医院的距离小于d,无需考虑。否则都要通过宾馆而行。

将每一个宾馆与源点连一条 流量为 h [ i ]  的边,表示宾馆的人数限制,

将每个医院与汇点连一条 流量为 cnt [ i ] 的边,表示需要到该医院上班的人数限制,

for一遍每一个医院和宾馆,判断距离<=d,则宾馆和医院可以联通,两者连一条流量为 inf  的边。

最后check是否满流即可,

总复杂度O(p*q*(n^3));注意数组开大。

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long ll;
const ll inf=1e15;
const int N=1e6+50;
int n,m,p,q,r,S,T;
struct Dinic{
    int head[N],dep[N];
    int ecnt; 
    struct edge{
        int v; ll flow;int next;
    }e[N*10];

    void init(){
        memset(head, -1, sizeof head);
        ecnt = 0;
    }

    void addedge(int u, int v,ll flow) {
    e[ecnt].v=v;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++;
    }
    bool BFS(){//分层图找增广路
        memset(dep,0,sizeof dep);
        queue<int>Q;
        Q.push(S);dep[S]=1;
        while(!Q.empty()){
            int u=Q.front();Q.pop();
            for(int i=head[u];~i;i=e[i].next){
                if(e[i].flow&&!dep[e[i].v]){
                    dep[e[i].v]=dep[u]+1;
                    Q.push(e[i].v);
                }
            }
        }
        return dep[T];
    }
    ll DFS(int u, ll f){//推进新流
        if(u==T||f==0)return f;
        ll w,used=0;
        for(int i=head[u];~i;i=e[i].next){
            if(e[i].flow&&dep[e[i].v]==dep[u]+1){
                w=DFS(e[i].v,min(f,e[i].flow));//多路增广
                e[i].flow-=w;e[i^1].flow+=w;
                used+=w;f-=w;
            }
        }
        if(!used)dep[u]=-1;//炸点优化
        return used;
    }
    ll maxflow() {
        ll ans=0;
        while(BFS()){
            ans+=DFS(S,inf);
        }
        return ans;
    }
}dinic;    
int X[N],Y[N],H[N],s[N],K[N];
struct edge{int v;ll w;int next;}e[N];
int ecnt,head[N];
void add(int u,int v,ll w){
    e[ecnt].v=v;e[ecnt].w=w;e[ecnt].next=head[u];head[u]=ecnt++;
}
struct node{int id;ll dis;
    node(int x,ll y){id=x,dis=y;}
    friend bool operator<(node a,node b){
    return a.dis>b.dis;
    }
};
bool done[150][N];ll dis[150][N];
void dijkstra(int s,int k){
    for(int i=1;i<=n+100;i++)dis[k][i]=inf,done[k][i]=0;
    dis[k][s]=0;
    priority_queue<node>Q;
    Q.push(node(s,0));
    while(!Q.empty()){
        int u=Q.top().id;Q.pop();
        if(done[k][u])continue;
        done[k][u]=1;
        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].v;
            if(done[k][v])continue;
        if(dis[k][v]>dis[k][u]+e[i].w){
            dis[k][v]=dis[k][u]+e[i].w;//更新距离
            Q.push(node(v,dis[k][v]));
            }
        }
    }
}
ll cnt[200];
bool check(ll d){
    memset(cnt,0,sizeof cnt);
    for(int i=1;i<=r;i++)if(dis[ K[i] ][ s[i] ]>d)cnt[ K[i] ]++;
    ll sum=0;
    for(int i=1;i<=p;i++)sum+=cnt[i];
    S=0,T=p+q+50;dinic.init();
    for(int i=1;i<=q;i++){
        dinic.addedge(S,i,H[i]);
        dinic.addedge(i,S,0);
    }
    for(int i=1;i<=q;i++){
        for(int j=1;j<=p;j++){
            if(dis[j][ Y[i] ]<=d){
                dinic.addedge(i,q+j,inf);
                dinic.addedge(q+j,i,0);
            }
        }
    }
    for(int i=1;i<=p;i++){
        dinic.addedge(q+i,T,cnt[i]);
        dinic.addedge(T,q+i,0);
    }
    if(dinic.maxflow()>=sum)return 1;
    else return 0;
}
int  main(){
    scanf("%d %d %d %d %d",&n,&m,&p,&q,&r);
    memset(head,-1,sizeof head);ecnt=0;

    for(int i=1,u,v;i<=m;i++){
        ll w;
        scanf("%d %d %lld",&u,&v,&w);
        add(u,v,w);add(v,u,w);
    }

    for(int i=1;i<=p;i++)scanf("%d",&X[i]);
    
    for(int i=1;i<=p;i++)dijkstra(X[i],i);

    for(int i=1;i<=q;i++)scanf("%d %d",&Y[i],&H[i]);
    for(int i=1;i<=r;i++)scanf("%d %d",&s[i],&K[i]);
    ll mid,L=0,R=(ll)1e14,ans=(ll)1e14;
    while(L<=R){
        mid=(L+R)>>1;
        if(check(mid))ans=mid,R=mid-1;
        else L=mid+1;
    }
    printf("%lld
",ans);
   // system("pause");
    return 0;
}
View Code

I - March of the Penguins

 POJ - 3498 

题意:给出n个岛屿,每个岛屿有ci 个企鹅,限制人数 lim i , 如果两个岛屿之间距离小于pos,那么企鹅可以相互到达,求

有多少个点可以作为终点,使得所有企鹅都可以跳到哪里。

做法:一一枚举终点,

每次建图:将 源点向所有左点连一条 ci ,表示最初有多少人,

     将所有点左点和右点连一条  lim i  ,表示这个点经过限制最多可以出去企鹅,

     枚举每两个点,如果距离小于pos,那么  i  的右点向 j 的左点连一条 inf 的边,表示 i 经过流量限制 可以到达 j 

                     那么  j  的右点向 i 的左点连一条 inf 的边,表示 j  经过流量限制可以到达 i

     最后将枚举的 k 点的 左点 向汇点连一条inf的边,表示不经过限制可以流到汇点。

然后判断是否满流即可。

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;
#define mp make_pair
typedef double db;
const int inf=0x3f3f3f3f;
const int N=1e5+5;
#define pb push_back
int n,m,S,T;
struct Dinic{
    int head[N],dep[N];
    int ecnt; 
    struct edge{
        int v,flow,next;
    }e[N*10];

    void init(){
        memset(head, -1, sizeof head);
        ecnt = 0;
    }

    void addedge(int u, int v, int  flow) {
    e[ecnt].v=v;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++;
    e[ecnt].v=u;e[ecnt].flow=0;e[ecnt].next=head[v];head[v]=ecnt++;
    }

    bool BFS(){//分层图找增广路
        memset(dep,0,sizeof dep);
        queue<int>Q;
        Q.push(S);dep[S]=1;
        while(!Q.empty()){
            int u=Q.front();Q.pop();
            for(int i=head[u];~i;i=e[i].next){
                if(e[i].flow&&!dep[e[i].v]){
                    dep[e[i].v]=dep[u]+1;
                    Q.push(e[i].v);
                }
            }
        }
        return dep[T];
    }
    int DFS(int u, int f){//推进新流
        if(u==T||f==0)return f;
        int w,used=0;
        for(int i=head[u];~i;i=e[i].next){
            if(e[i].flow&&dep[e[i].v]==dep[u]+1){
                w=DFS(e[i].v,min(f,e[i].flow));//多路增广
                e[i].flow-=w;e[i^1].flow+=w;
                used+=w;f-=w;
            }
        }
        if(!used)dep[u]=-1;//炸点优化
        return used;
    }
    int maxflow() {
        int ans=0;
        while(BFS()){
            ans+=DFS(S,inf);
        }
        return ans;
    }
}dinic;  
struct point{db x,y;}P[100000];
db dis(point a,point b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
int C[100000],lim[100000];
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        db pos;
        scanf("%d %lf",&n,&pos);
        int sum=0;
        for(int i=1;i<=n;i++)
        scanf("%lf %lf %d %d",&P[i].x,&P[i].y,&C[i],&lim[i]),sum+=C[i];
        vector<int>ans;
        S=0,T=2*n+50;
        for(int k=1;k<=n;k++){
            dinic.init();
            for(int i=1;i<=n;i++){
                dinic.addedge(S,i,C[i]);
                dinic.addedge(i,n+i,lim[i]);
            }
            for(int i=1;i<=n;i++){
                for(int j=i+1;j<=n;j++){
                    if(dis(P[i],P[j])<=pos){
                        dinic.addedge(i+n,j,inf);
                        dinic.addedge(j+n,i,inf);
                    }
                }
            }
            dinic.addedge(k,T,inf);
            if(dinic.maxflow()==sum)ans.pb(k);
        }
        int len=ans.size();
        if(!len){puts("-1");continue;}
        for(int i=0;i<len;i++){
            printf("%d%c",ans[i]-1,i==len-1?'
':' ');
        }

    }
      
        // system("pause");
    return 0;
}

// 做法应该就是枚举每一个点,当做汇点,判断是否满流
View Code
原文地址:https://www.cnblogs.com/littlerita/p/12764681.html