网络流

最小割

最小割为割掉后使s、t不连通的边的容量和的最小值,数值上等于最大流

inline void add(int a,int b,int c){
    e[++tot].t=b;e[tot].flow=c;e[tot].nxt=head[a];head[a]=tot;
    e[++tot].t=a;e[tot].flow=0;e[tot].nxt=head[b];head[b]=tot;
}inline bool bfs(){
    memset(dep,0,sizeof(dep));
    dep[s]=1;
    queue<int>q;q.push(s);cur[s]=head[s];
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].t;
            if(dep[v]||!e[i].flow)  continue;
            dep[v]=dep[u]+1;cur[v]=head[v];
            if(v==t)  return 1;
            q.push(v);
        }
    }return 0;
}int dfs(int u,int flow){
    if(u==t)  return flow;
    int rest=flow;
    for(int &i=cur[u];i&&rest;i=e[i].nxt){
        int v=e[i].t;
        if(dep[v]!=dep[u]+1||!e[i].flow)  continue;
        int tmp=dfs(v,min(rest,e[i].flow));
        if(!tmp){dep[v]=0;continue;}
        rest-=tmp;e[i].flow-=tmp;
        e[((i-1)^1)+1].flow+=tmp;
        if(!rest)  return flow;
    }return flow-rest;
}inline int Dinic(){
    int ans=0;
    while(bfs())  ans+=dfs(s,inf);
    return ans;
}
Dinic(最大流)

总结几种经典建图

1.最大权闭合子图

以下例题极其相似,若只学习套路看例题一即可。

例题1:最大获利

正权点连s,负权点连t,容量为权
原边容量inf,使依赖关系无法分离
ans=正权和-最小割

割去的边为未选择的正权点和已选择的正点依赖的负权点

以样例为例

1-5中转站,6-10用户群

例题2:植物大战僵尸

正负点与原边关系很明显,注意删去原图的环,环内点以及指向环的点全部不可到达

通过环内点与t连边inf,强制割掉环内正权点

注意网络流中边指向被依赖点,拓扑中边从被依赖点出发

以样例为例

4 5及3排1 2列,成环不可获得。割掉3后答案为230-100-100-5=25

 

#include<bits/stdc++.h>
using namespace std;
const int MAX=660;
const int inf=1e9;
const int N=3e5;
int n,m,a[MAX],x,y,z,s,t,head[MAX],tot,dep[MAX],cur[MAX],sum,w;
int in[MAX],ans;
struct edge{
    int t,flow,nxt;
} e[N];
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return x*f;
}
inline void add(int,int,int);
inline int bfs();
int dfs(int,int);
inline int Dinic();
int main(){
    n=read();m=read();t=(s=n*m)+1;
    for(int i=0;i<n;++i)
        for(int j=0;j<m;++j){
            x=i*m+j;a[x]=read();
            if(a[x]>0){
                ++in[s];sum+=a[x];
                add(s,x,a[x]);add(x,s,0);
            }else{
                ++in[x];
                add(x,t,-a[x]);add(t,x,0);
            }    
            if(j!=m-1){
                add(x,x+1,inf);add(x+1,x,0);
                in[x]++;
            }w=read();
            for(int k=1;k<=w;++k){
                z=read()*m+read();
                add(z,x,inf);add(x,z,0);
                ++in[z];
            }
        }
    queue<int>q;
    for(int i=0;i<=t;++i)
        if(!in[i])  q.push(i);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].nxt){
            if(i&1)  continue;
            int v=e[i].t;in[v]--;
            if(!in[v])  q.push(v);
        }
    }
    for(int i=0;i<s;++i)
        if(in[i]){
            add(i,t,inf);add(t,i,0);
        }  
    printf("%d\n",sum-Dinic());
}
inline void add(int a,int b,int c){
    e[++tot].t=b;e[tot].flow=c;e[tot].nxt=head[a];head[a]=tot;
}inline int bfs(){
    memset(dep,0,sizeof(dep));
    queue<int>q;q.push(s);dep[s]=1;cur[s]=head[s];
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].t;
            if(!e[i].flow||dep[v])  continue;
            dep[v]=dep[u]+1;cur[v]=head[v];
            if(v==t)  return 1;
            q.push(v);
        }
    }return 0;
}int dfs(int u,int flow){
    if(u==t||!flow)  return flow;
    int rest=flow;
    for(int &i=cur[u];i&&rest;i=e[i].nxt){
        int v=e[i].t;
        if(!e[i].flow||dep[v]!=dep[u]+1)  continue;
        int tmp=dfs(v,min(rest,e[i].flow));
        if(!tmp){dep[v]=0;continue;}
        rest-=tmp;e[i].flow-=tmp;
        e[((i-1)^1)+1].flow+=tmp;
    }return flow-rest;
}inline int Dinic(){
    while(bfs())  ans+=dfs(s,inf);
    return ans;
}
View Code

例题3:寿司餐厅

看错题哩,做了一天看题解才发现哩(其实我觉得是题意不明)

区分种类和代号!!!

首先,正权di,j指向di+1,j  di,j-1  依赖∑∑dl,r(i<=l<=j,l<=r<=j)  且每个d只贡献一次

其次,负权x每种寿司也只贡献一次,如选择d2,3 d3,4 ,a3只被减一次。可使di,i连边t容量ai

而对于同代号首次贡献mx2,使每个di,i连边代号点,代号点连边t容量mx2

以样例为例  

省略了与s,t连边。1为d1,1  2为d1,2   6为d2,1  16、18、19为代号

读清题后,就是简单套路

#include<bits/stdc++.h>
using namespace std;
const int MAX=20010;
const int inf=1e9;
const int N=3e5;
int n,m,x,a[MAX],mp[110][110],s,t,head[MAX],cur[MAX],tot,dep[MAX],sum,ans;
int cnt;
struct edge{
    int t,flow,nxt;
} e[N];
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return x*f;
}
inline void add(int,int,int);
inline int bfs();
int dfs(int,int);
inline int Dinic();
int main(){
    n=read();m=read();
    for(int i=1;i<=n;++i)  a[i]=read();
    for(int i=1;i<=n;++i)
        for(int j=i;j<=n;++j)
            mp[i][j]=++cnt;
    t=(s=cnt+1000+1)+1;
    for(int i=1;i<=n;++i)
        for(int j=i;j<=n;++j){
            x=read();
            if(x>0)  add(s,mp[i][j],x);
            else  add(mp[i][j],t,-x);
            if(i!=j){
                add(mp[i][j],mp[i][j-1],inf);
                add(mp[i][j],mp[i+1][j],inf);    
            }else{
                add(mp[i][i],t,a[i]);
                add(mp[i][i],cnt+a[i],inf);
            } 
        }
    for(int i=1;i<=1000;++i)  add(cnt+i,t,m*i*i);
    printf("%d",sum-Dinic());
}
inline void add(int a,int b,int c){
    if(a==s)  sum+=c;
e[++tot].t=b;e[tot].flow=c;e[tot].nxt=head[a];head[a]=tot;
    e[++tot].t=a;e[tot].flow=0;e[tot].nxt=head[b];head[b]=tot;
}inline int bfs(){
    memset(dep,0,sizeof(dep));
    queue<int>q;q.push(s);dep[s]=1;cur[s]=head[s];
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].t;
            if(!e[i].flow||dep[v])  continue;
            dep[v]=dep[u]+1;cur[v]=head[v];
            if(v==t)  return 1;
            q.push(v);
        }
    }return 0;
}int dfs(int u,int flow){
    if(u==t||!flow)  return flow;
    int rest=flow;
    for(int &i=cur[u];i&&rest;i=e[i].nxt){
        int v=e[i].t;
        if(!e[i].flow||dep[v]!=dep[u]+1)  continue;
        int tmp=dfs(v,min(rest,e[i].flow));
        if(!tmp){dep[v]=0;continue;}
        rest-=tmp;e[i].flow-=tmp;
        e[((i-1)^1)+1].flow+=tmp;
    }return flow-rest;
}inline int Dinic(){
    while(bfs())  ans+=dfs(s,inf);
    return ans;
}
View Code

2.网格图

以一个图形将几个网格集为一体,一个网格可能多次出现,求最小代价

一般染色,将可能多次出现的网格贡献放在s,t,只出现一次的放在中间。有时也会费用流

例题1:老C的方块

发现黑白染色不能满足要求,于是染4色,对网格编号

奇数列1,2,3,4,1,2,3,4

偶数列4,3,2,1,4,3,2,1

保证每个图形里都有1,2,3,4。发现1,2中间夹着蓝线,即1,2不会同时移除,于是缩为一条边,放在中间。3,4放两边连st,1、2连周围3个3,4

形如

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5+10;
const int inf=1e9;
int C,R,n,s,t;
struct block{
    int x,y,w;
} c[MAX];
struct edge{
    int t,flow,nxt;
} e[MAX*4];
int dep[MAX],head[MAX],cur[MAX],tot;
queue<int>q;
map<pair<int,int>,int>mp;
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();}
    return x*f;
}
inline void add(int,int,int);
inline bool bfs();
int dfs(int,int);
inline int Dinic();
int main(){
    // freopen("1.txt","r",stdin);
    C=read();R=read();n=read();
    t=(s=n+1)+1;
    for(int i=1;i<=n;++i){
        c[i]={read(),read(),read()};
        mp[{c[i].y,c[i].x}]=i;
    }for(int i=1;i<=n;++i){
        int y=c[i].x,x=c[i].y;
        if(x&1){
            if((y%4==1)&&mp[{x,y+1}]){
                add(i,mp[{x,y+1}],min(c[i].w,c[mp[{x,y+1}]].w));
            }if(y%4==2){
                if(mp[{x,y+1}])  add(i,mp[{x,y+1}],inf);
                if(mp[{x+1,y}])  add(i,mp[{x+1,y}],inf);
                if(mp[{x-1,y}])  add(i,mp[{x-1,y}],inf);
            }if(y%4==3)  add(i,t,c[i].w);
            if(y%4==0){
                add(s,i,c[i].w);
                if(mp[{x,y+1}])  add(i,mp[{x,y+1}],inf);
                if(mp[{x+1,y}])  add(i,mp[{x+1,y}],inf);
                if(mp[{x-1,y}])  add(i,mp[{x-1,y}],inf);
            }
        }if(!(x&1)){
            if(y%4==0&&mp[{x,y-1}]){
                add(i,mp[{x,y-1}],min(c[i].w,c[mp[{x,y-1}]].w));
            }if(y%4==3){
                if(mp[{x,y-1}])  add(i,mp[{x,y-1}],inf);
                if(mp[{x+1,y}])  add(i,mp[{x+1,y}],inf);
                if(mp[{x-1,y}])  add(i,mp[{x-1,y}],inf);
            }if(y%4==2)  add(i,t,c[i].w);
            if(y%4==1){
                add(s,i,c[i].w);
                if(mp[{x,y-1}])  add(i,mp[{x,y-1}],inf);
                if(mp[{x+1,y}])  add(i,mp[{x+1,y}],inf);
                if(mp[{x-1,y}])  add(i,mp[{x-1,y}],inf);
            }
        }
    }printf("%d",Dinic());
}
inline void add(int a,int b,int c){
    e[++tot]={b,c,head[a]};head[a]=tot;
    e[++tot]={a,0,head[b]};head[b]=tot;
}inline bool bfs(){
    memset(dep,0,sizeof(dep));
    while(!q.empty())  q.pop();
    dep[s]=1;q.push(s);cur[s]=head[s];
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].t;
            if(dep[v]||!e[i].flow)  continue;
            dep[v]=dep[u]+1;cur[v]=head[v];
            if(v==t)  return 1;
            q.push(v);
        }
    }return 0;
}int dfs(int u,int flow){
    if(u==t)  return flow;
    int rest=flow;
    for(int &i=cur[u];i;i=e[i].nxt){
        int v=e[i].t;
        if(dep[v]!=dep[u]+1||!e[i].flow)  continue;
        int tmp=dfs(v,min(rest,e[i].flow));
        if(!tmp){dep[v]=0;continue;}
        rest-=tmp;e[i].flow-=tmp;
        e[((i-1)^1)+1].flow+=tmp;
        if(!rest)  return flow;
    }return flow-rest;
}inline int Dinic(){
    int ans=0;
    while(bfs())  ans+=dfs(s,inf);
    return ans;
}
View Code

3.离散变量模型

例题1:Course Selection

依赖+多选一。很容易想到费用流 ,但进行x到M-x的转换,串联最小割也可以实现多选一。依赖其实就是使A在B之前选择

然后使用最小割离散变量模型:对每个物品的每个状态建店,每个状态向上一个状态连边,流量为此状态的贡献,最后状态向汇点t连无穷边。对于依赖(A,B),源点s向B状态1连无穷边,每个状态i,(A,i)向(B,i+1)连无穷边

1
3 2 2
70 100
100 80
100 90
1 2
1 3
样例

为例建图

#include<bits/stdc++.h>
using namespace std;
const int MAX=10010,inf=1e8;
int T,n,m,K,x,y,ans,tot,head[MAX],s,t,dep[MAX],cur[MAX];
struct edge{
    int t,nxt,flow;
} e[MAX*200];
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();}
    return x*f;
}
inline void add(int,int,int);
inline void Dinic();
signed main(){ 
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    T=read();
    while(T--){
        n=read();m=read();K=read();t=(s=n*m+1)+1;
        for(int i=1;i<=t;++i)  head[i]=0;tot=0;
        for(int i=1;i<=n;++i)  
            for(int j=1;j<=m;++j){
                if(j==1)  add(s,i,100-read());
                else  add((j-2)*n+i,(j-1)*n+i,100-read());
                if(j==m)  add((j-1)*n+i,t,inf);
            } 
        for(int i=1;i<=K;++i){
            x=read();y=read();add(s,y,inf);
            for(int j=1;j<m;++j)
                add((j-1)*n+x,j*n+y,inf);
        }Dinic();if(ans>100*n)  printf("-1\n");
        else  printf("%d\n",100*n-ans);
    
    }
}
inline void add(int a,int b,int c){
    if(c>100)  c=inf;
    // cout<<a<<" "<<b<<" "<<c<<endl;
    e[++tot].t=b;e[tot].flow=c;e[tot].nxt=head[a];head[a]=tot;
    e[++tot].t=a;e[tot].flow=0;e[tot].nxt=head[b];head[b]=tot;
}inline bool bfs(){
    memset(dep,0,sizeof(dep));
    dep[s]=1;
    queue<int>q;q.push(s);cur[s]=head[s];
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].t;
            if(dep[v]||!e[i].flow)  continue;
            dep[v]=dep[u]+1;cur[v]=head[v];
            if(v==t)  return 1;
            q.push(v);
        }
    }return 0;
}int dfs(int u,int flow){
    if(u==t)  return flow;
    int rest=flow;
    for(int &i=cur[u];i&&rest;i=e[i].nxt){
        int v=e[i].t;
        if(dep[v]!=dep[u]+1||!e[i].flow)  continue;
        int tmp=dfs(v,min(rest,e[i].flow));
        if(!tmp){dep[v]=0;continue;}
        rest-=tmp;e[i].flow-=tmp;
        e[((i-1)^1)+1].flow+=tmp;
        if(!rest)  return flow;
    }return flow-rest;
}inline void Dinic(){
    ans=0;
    while(bfs())  ans+=dfs(s,inf);
}
View Code

例题2:切糕

 是不是这个题更典

要求相邻点割边距离不超过D的前提下代价最小

在每个物品每个状态建边的基础上,状态i向相邻状态i-D连无穷边

以D=2为例

 如果选择距离超过2的蓝边,橙边使得无法形成割

#include<bits/stdc++.h>
using namespace std;
const int MAX=500060;
const int N=1e6+10;
const int inf=1<<28;
int n,m,head[MAX],tot,cur[MAX],dep[MAX],s,t,ans,sum,cnt;
int R,x,D;
struct edge{
    int t,nxt,flow;
} e[MAX];
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return x*f;
}
inline void add(int,int,int);
inline int Dinic();
inline int bfs();
int dfs(int,int);
signed main(){
//    freopen("in.txt","r",stdin);
    n=read();m=read();R=read();
    D=read();t=(s=n*m*R+1)+1;
    for(int i=1;i<=R;++i)
        for(int j=1;j<=n;++j)
            for(int k=1;k<=m;++k){
                x=read();int d=(i-1)*n*m+(j-1)*m+k;
                if(i==1)  add(s,d,inf);
                if(i!=R)  add(d,d+n*m,x);
                else  add(d,t,x);
                if(i-D<1)  continue;
                int l=d-D*n*m;
                if(j>1)  add(d,l-m,inf);
                if(k>1)  add(d,l-1,inf);
                if(j<n)  add(d,l+m,inf);
                if(k<m)  add(d,l+1,inf);//k<n
            }
    printf("%d\n",Dinic());
}
inline void add(int a,int b,int c){
    e[++tot].flow=c;e[tot].nxt=head[a];e[tot].t=b;head[a]=tot;
    e[++tot].flow=0;e[tot].nxt=head[b];e[tot].t=a;head[b]=tot;
}inline int Dinic(){
    ans=0;
    while(bfs())  ans+=dfs(s,INT_MAX);
    return ans;
}inline int bfs(){
    memset(dep,0,sizeof(dep));
    queue<int>q;q.push(s);
    dep[s]=1;cur[s]=head[s];
    while(!q.empty()){
        int u=q.front(),v;q.pop();
        for(int i=head[u];i;i=e[i].nxt){
            v=e[i].t;
            if(e[i].flow<=0||dep[v])  continue;
            dep[v]=dep[u]+1;cur[v]=head[v];
            if(v==t)  return 1;
            q.push(v);
        }
    }return 0;
}int dfs(int u,int flow){
    if(u==t||!flow)  return flow;
    int rest=flow;
    for(int &i=cur[u];i&&rest;i=e[i].nxt){
        int v=e[i].t;
        if(e[i].flow<=0||dep[v]!=dep[u]+1)  continue;
        int tmp=dfs(v,min(rest,e[i].flow));
        if(!tmp){dep[v]=0;continue;}
        rest-=tmp;e[i].flow-=tmp;
        e[((i-1)^1)+1].flow+=tmp;
    }return flow-rest;
}
View Code

费用流

#include<bits/stdc++.h>
using namespace std;
const int MAX=210010;
const int inf=1<<28;
int n,m,s,t,tot,head[410],x,y,z;
int dis[MAX],pre[MAX],vis[MAX],ans,cost,flo[MAX];
struct edge{
    int t,nxt,flow,val;
} e[MAX];
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return x*f;
}
inline void add(int,int,int,int);
inline bool spfa();
inline void EK();
int main(){
    n=read();m=read();s=1;t=n;
    for(int i=2;i<n;++i)  add(i,n+i,1,0);
    for(int i=1;i<=m;++i){
        x=read();y=read();z=read();
        if(x!=1)  x+=n;add(x,y,1,z);
    }EK();
}
inline void add(int a,int b,int c,int v){
    e[++tot].t=b;e[tot].flow=c;e[tot].val=v;
    e[tot].nxt=head[a];head[a]=tot;
    e[++tot].t=a;e[tot].flow=0;e[tot].val=-v;
    e[tot].nxt=head[b];head[b]=tot;
}
inline bool spfa(){
    for(int i=1;i<2*n;++i)  vis[i]=0,dis[i]=inf;
    vis[s]=1;dis[s]=0;pre[t]=-1;flo[s]=dis[t];
    queue<int>q;q.push(s);
    while(!q.empty()){
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].t;
            if(dis[v]>dis[u]+e[i].val&&e[i].flow){
                flo[v]=min(flo[u],e[i].flow);
                pre[v]=i;dis[v]=dis[u]+e[i].val;
                if(!vis[v]){
                    vis[v]=1;q.push(v);
                }
            }
        }
    }return dis[t]!=flo[s];
}inline void EK(){
    while(spfa()){
        ans+=flo[t];cost+=flo[t]*dis[t];
        int now=t;
        while(now!=s){
            e[pre[now]].flow-=flo[t];
            e[((pre[now]-1)^1)+1].flow+=flo[t];
            now=e[((pre[now]-1)^1)+1].t;
        }
    }printf("%d %d\n",ans,cost);
}
View Code
#include<bits/stdc++.h>
using namespace std;
const int MAX=400010;
#define int long long
const int inf=1e17;
int dis[MAX],pre[MAX],vis[MAX],ans,cost,flo[MAX],h[MAX];priority_queue<pair<int,int> >q;
struct edge{
    int t,nxt,flow,val;
} e[MAX*20];
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return x*f;
}
inline void add(int,int,int,int);
inline void EK();
inline void add(int a,int b,int c,int v){
//    cout<<a<<" "<<b<<" "<<v<<endl;
    e[++tot].t=b;e[tot].flow=c;e[tot].val=v;
    e[tot].nxt=head[a];head[a]=tot;
    e[++tot].t=a;e[tot].flow=0;e[tot].val=-v;
    e[tot].nxt=head[b];head[b]=tot;
}
inline bool dij(){
    for(int i=1;i<=t;++i)  vis[i]=0,dis[i]=inf;
    dis[s]=0;pre[t]=-1;flo[s]=dis[t];
    q.push({0,s});
    while(!q.empty()){
        int u=q.top().second;q.pop();
        if(vis[u])  continue;
        vis[u]=1;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].t;
            if(dis[v]>dis[u]+e[i].val+h[u]-h[v]&&e[i].flow){
                flo[v]=min(flo[u],e[i].flow);
                pre[v]=i;dis[v]=dis[u]+e[i].val+h[u]-h[v];
                q.push({-dis[v],v});
            }
        }
    }return dis[t]!=flo[s];
}inline void EK(){
    while(dij()){
        for(int i=1;i<=t;++i)  h[i]+=dis[i];
        ans+=flo[t];cost+=flo[t]*h[t];
        int now=t;
        while(now!=s){
            e[pre[now]].flow-=flo[t];
            e[((pre[now]-1)^1)+1].flow+=flo[t];
            now=e[((pre[now]-1)^1)+1].t;
        }
    }printf("%lld\n",cost);
}
dij

上下界

无源汇有上下界可行流

#include<bits/stdc++.h>
using namespace std;
const int MAX=21010;
const int inf=1e9;
#define int long long
int n,m,T,q,tot,s,t,tf[MAX],head[MAX],cur[MAX],dep[MAX],x,y,z,low[MAX],sum,ans[MAX];
struct edge{
    int t,flow,nxt;
} e[MAX];
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();}
    return x*f;
}
inline void add(int,int,int);
inline bool bfs();
int dfs(int,int);
inline int Dinic();
signed main(){
    n=read();m=read();
    t=(s=n+1)+1;
    for(int i=1;i<=m;++i){
        x=read();y=read();low[i]=read();z=read();
        add(x,y,z-low[i]);tf[x]-=low[i];tf[y]+=low[i];
    }sum=0;
    for(int i=1;i<=n;++i){
        if(tf[i]<0)  add(i,t,-tf[i]);
        else{
            sum+=tf[i];add(s,i,tf[i]);
        }
    }if(Dinic()==sum){
        printf("YES\n");
        for(int i=1;i<=n;++i)
            for(int j=head[i];j;j=e[j].nxt){
                if(j>m*2||(j&1))  continue;
                ans[j>>1]=e[j].flow+low[j>>1];
            }
        for(int i=1;i<=m;++i)  printf("%d\n",ans[i]);
    }else  printf("NO\n");
}
inline void add(int a,int b,int c){
    e[++tot].t=b;e[tot].flow=c;e[tot].nxt=head[a];head[a]=tot;
    e[++tot].t=a;e[tot].flow=0;e[tot].nxt=head[b];head[b]=tot;
}inline bool bfs(){
    memset(dep,0,sizeof(dep));dep[s]=1;
    queue<int>q;q.push(s);cur[s]=head[s];
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].t;
            if(dep[v]||!e[i].flow)  continue;
            dep[v]=dep[u]+1;cur[v]=head[v];
            if(v==t)  return 1;
            q.push(v);
        }
    }return 0;
}int dfs(int u,int flow){
    if(u==t)  return flow;
    int rest=flow;
    for(int &i=cur[u];i&&rest;i=e[i].nxt){
        int v=e[i].t;
        if(dep[v]!=dep[u]+1||!e[i].flow)  continue;
        int tmp=dfs(v,min(rest,e[i].flow));
        if(!tmp){dep[v]=0;continue;}
        rest-=tmp;e[i].flow-=tmp;
        e[((i-1)^1)+1].flow+=tmp;
    }return flow-rest;
}inline int Dinic(){
    int ans=0;
    while(bfs())  ans+=dfs(s,inf);
    return ans;
}
View Code

有源汇有上下界最大流

#include<bits/stdc++.h>
using namespace std;
const int MAX=21010;
const int inf=1e9;
#define int long long
int n,m,T,tot,s,t,tf[MAX],head[MAX],cur[MAX],dep[MAX],x,y,z,low[MAX],sum,ans[MAX];
int ss,tt,res,qs,qt;
struct edge{
    int t,flow,nxt;
} e[MAX];
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();}
    return x*f;
}
inline void add(int,int,int);
inline bool bfs();
int dfs(int,int);
inline int Dinic();
signed main(){
    n=read();m=read();s=read();t=read();
    tt=(ss=n+1)+1;
    for(int i=1;i<=m;++i){
        x=read();y=read();low[i]=read();z=read();
        add(x,y,z-low[i]);tf[x]-=low[i];tf[y]+=low[i];
    }sum=0;
    for(int i=1;i<=n;++i){
        if(tf[i]<0)  add(i,tt,-tf[i]);
        else{
            sum+=tf[i];add(ss,i,tf[i]);
        }
    }add(t,s,inf);qs=s;qt=t;
    s=ss;t=tt;
    if(Dinic()==sum){
        s=qs;t=qt;
        printf("%d\n",Dinic());
    }else  printf("please go home to sleep\n");
}
inline void add(int a,int b,int c){
    e[++tot].t=b;e[tot].flow=c;e[tot].nxt=head[a];head[a]=tot;
    e[++tot].t=a;e[tot].flow=0;e[tot].nxt=head[b];head[b]=tot;
}inline bool bfs(){
    memset(dep,0,sizeof(dep));dep[s]=1;
    queue<int>q;q.push(s);cur[s]=head[s];
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].t;
            if(dep[v]||!e[i].flow)  continue;
            dep[v]=dep[u]+1;cur[v]=head[v];
            if(v==t)  return 1;
            q.push(v);
        }
    }return 0;
}int dfs(int u,int flow){
    if(u==t)  return flow;
    int rest=flow;
    for(int &i=cur[u];i&&rest;i=e[i].nxt){
        int v=e[i].t;
        if(dep[v]!=dep[u]+1||!e[i].flow)  continue;
        int tmp=dfs(v,min(rest,e[i].flow));
        if(!tmp){dep[v]=0;continue;}
        rest-=tmp;e[i].flow-=tmp;
        e[((i-1)^1)+1].flow+=tmp;
    }return flow-rest;
}inline int Dinic(){
    int res=0;
    while(bfs())  res+=dfs(s,inf);
    return res;
}
View Code

有源汇有上下界最小流

#include<bits/stdc++.h>
using namespace std;
const int MAX=125010;
const int N=5e4+10;
const int inf=1e9;
//#define int long long
int n,m,T,tot,s,t,tf[N],head[N],cur[N],dep[N],x,y,z,low[MAX],sum;
int ss,tt,qs,qt;
struct edge{
    int t,flow,nxt;
} e[MAX+N<<1];
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();}
    return x*f;
}
inline void add(int,int,int);
inline bool bfs();
int dfs(int,int);
inline int Dinic();
inline void del(int x){
    for(int i=head[x];i;i=e[i].nxt)  e[i].flow=e[((i-1)^1)+1].flow=0;
}
signed main(){
    n=read();m=read();s=read();t=read();
    tt=(ss=n+1)+1;
    for(int i=1;i<=m;++i){
        x=read();y=read();low[i]=read();z=read();
        add(x,y,z-low[i]);tf[x]-=low[i];tf[y]+=low[i];
    }sum=0;
    for(int i=1;i<=n;++i){
        if(tf[i]<0)  add(i,tt,-tf[i]);
        else{
            sum+=tf[i];add(ss,i,tf[i]);
        }
    }add(t,s,inf);qs=s;qt=t;
    s=ss;t=tt;
    if(Dinic()==sum){
        s=qt;t=qs;sum=e[tot].flow;
        e[tot].flow=e[tot-1].flow=0;
        del(ss);del(tt);
        printf("%d\n",sum-Dinic());
    }else  printf("please go home to sleep\n");
}
inline void add(int a,int b,int c){
    e[++tot].t=b;e[tot].flow=c;e[tot].nxt=head[a];head[a]=tot;
    e[++tot].t=a;e[tot].flow=0;e[tot].nxt=head[b];head[b]=tot;
}inline bool bfs(){
    memset(dep,0,sizeof(dep));
    dep[s]=1;
    queue<int>q;q.push(s);cur[s]=head[s];
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].t;
            if(dep[v]||!e[i].flow)  continue;
            dep[v]=dep[u]+1;cur[v]=head[v];
            if(v==t)  return 1;
            q.push(v);
        }
    }return 0;
}int dfs(int u,int flow){
    if(u==t)  return flow;
    int rest=flow;
    for(int &i=cur[u];i&&rest;i=e[i].nxt){
        int v=e[i].t;
        if(dep[v]!=dep[u]+1||!e[i].flow)  continue;
        int tmp=dfs(v,min(rest,e[i].flow));
        if(!tmp){dep[v]=0;continue;}
        rest-=tmp;e[i].flow-=tmp;
        e[((i-1)^1)+1].flow+=tmp;
        if(!rest)  return flow;
    }return flow-rest;
}inline int Dinic(){
    int ans=0;
    while(bfs())  ans+=dfs(s,inf);
    return ans;
}
View Code

有源汇上下界最小费用可行流

#include<bits/stdc++.h>
using namespace std;
const int MAX=30010;
const int N=4010;
bool vis[N];
int dis[N],pre[N],ss,tt,p,m,f,M,S,n,tot,s,t,head[N],x;
const int inf1=1e9;
#define ll long long

const ll inf=1e17;
struct edge{
    int t,nxt,val;
    ll flow; 
} e[MAX];
ll flo[N],ans,cost;
queue<int>q;
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^'0');c=getchar();}
    return x*f;
}
inline void add(int,int,ll,int);
inline bool spfa();
inline void EK();
signed main(){
//    freopen("1.txt","r",stdin);
    n=read();t=(s=n<<1|1)+1;
    tt=(ss=t+1)+1;
    for(int i=1;i<=n;++i){
        x=read();add(i,n+i,inf,0);
        add(i+n,t,inf,0);
        add(i,tt,x,0);add(ss,n+i,x,0);
    }p=read();m=read();f=read();M=read();S=read();
    add(s,1,inf,p);
    for(int i=1;i<=n;++i){
        if(i!=n)  add(i,i+1,inf,0);
        if(i+m<=n)  add(n+i,i+m,inf,f);
        if(i+M<=n)  add(n+i,i+M,inf,S);
    }add(t,s,inf,0);
    s=ss;t=tt;EK();
    printf("%lld",cost);
}
inline void add(int a,int b,ll c,int v){
    e[++tot].t=b;e[tot].flow=c;e[tot].val=v;e[tot].nxt=head[a];head[a]=tot;
    e[++tot].t=a;e[tot].flow=0;e[tot].val=-v;e[tot].nxt=head[b];head[b]=tot;
}inline bool spfa(){
    for(int i=1;i<=t;++i)  vis[i]=0,dis[i]=inf1;
    vis[s]=1;dis[s]=0;pre[t]=-1;flo[s]=inf;
    q.push(s);
    while(!q.empty()){
        int u=q.front();q.pop();vis[u]=0;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].t;
            if(dis[v]>dis[u]+e[i].val&&e[i].flow){
                flo[v]=min(flo[u],e[i].flow);
                pre[v]=i;dis[v]=dis[u]+e[i].val;
                if(!vis[v]){vis[v]=1;q.push(v);}
            }
        }
    }return dis[t]!=inf1;
}inline void EK(){
    while(spfa()){
        ans+=flo[t];cost+=1ll*flo[t]*dis[t]; 
        int now=t;
        while(now!=s){
            e[pre[now]].flow-=flo[t];
            e[((pre[now]-1)^1)+1].flow+=flo[t];
            now=e[((pre[now]-1)^1)+1].t;
        }
    }
}
View Code

 

posted @ 2024-02-29 16:31  yisiwunian  阅读(39)  评论(0编辑  收藏  举报