很久之前就想要学习网络流了,最近也刚学了网络流,于是便有了本篇博客。

关于算法:

网络流模板
个人喜欢用dinic跑非一次性建边的网络流,用isap跑一次性建边的网络流,用zkw跑费用流,至于HLPP我还不会
当然网络流还是要靠建图。


网络流24题:(仅包含部分题目)

P2756 飞行员配对方案问题

显然的一个二分图匹配问题,还要输出方案;

我们考虑建图,首先一个飞行员只能匹配一个飞行员,所以我们从S向一类飞行员连边,从另一类飞行员向T连边,边权为1,然后对于能匹配的飞行员我们连接1或者inf的边(本体都可),完成建图。

关于输出方案:考虑残量网络上的反向边发生流量改变即可。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <cmath>
using namespace std;
#define I inline
#define R register
#define inf 1073742823
#define FOR(i,begin,end) for(R int i=begin;i<=end;++i)
#define ROF(i,begin,end) for(R int i=begin;i>=end;--i)
#include<queue>
namespace IO{
    char buf[1<<21],*pa=buf,*pb=buf;
    char buffer[1<<21];int p1=-1;const int p2=(1<<21)-1; 
    I char gc(){return pa==pb&&(pb=(pa=buf)+fread(buf,1,1<<21,stdin),pa==pb)?EOF:*pa++;}
    template<class T>I void read(T &x){
        x=0;R int y=0;R char ch=gc();
        for(;!isdigit(ch);ch=gc())y=ch=='-';
        for(;isdigit(ch);ch=gc())x=x*10+(ch^48);
        (y)&&(x=-x);return;}
    I void flush(){fwrite(buffer,1,p1+1,stdout);p1=-1;return;}
    I void pc(int ch){if(p1==p2)flush();buffer[++p1]=ch;return;}
    template<class T>I void write(T x){
        static char buf[20];static int len =-1;
        if(x>=0){do buf[++len]=(x%10)^48,x/=10;while(x);}
        else{pc('-');do buf[++len]=(-(x%10))^48,x/=10;while(x);}
        while(len>=0)pc(buf[len--]);
        return;}
}
using namespace IO;
int tot,s,t;
const int maxn = 50000+50;
const int maxm = 200000+200;
#define ci const int &
struct edge{
    int u,v,f,n;
    I edge(ci uu =0,ci vv=0,ci ff=0,ci nn=0):u(uu),v(vv),f(ff),n(nn){}
}e[maxm];
int cnt=1,head[maxn],cur[maxn],gap[maxn],dis[maxn],mxflow=0;
I void add(ci u,ci v,ci f){
    e[++cnt]=edge(u,v,f,head[u]),head[u]=cnt;
    e[++cnt]=edge(v,u,0,head[v]),head[v]=cnt;
}
I void bfs(){
    queue<int>q;q.push(t),gap[dis[t]=1]++;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int ee=head[u],v;v=e[ee].v,ee;ee=e[ee].n)
        if(!dis[v])gap[dis[v]=dis[u]+1]++,q.push(v);
    }
}
int dfs(ci u,ci flow){
    if(u==t&&((mxflow+=flow)||1))return flow;
    int us=0,f;
    for(int ee=cur[u],v;cur[u]=ee,v=e[ee].v,ee;ee=e[ee].n)
        if(dis[v]==dis[u]-1){
            if(f=dfs(v,min(flow-us,e[ee].f)))
                e[ee].f-=f,e[ee^1].f+=f,us+=f;
            if(us==flow)return flow;
        }
    (--gap[dis[u]])?(++gap[++dis[u]]):dis[s]=tot+1;
    return us;
}
I int isap(){
    bfs();
    while(dis[s]<=tot+1)
        memcpy(cur,head,sizeof head),
        dfs(s,inf);
    return mxflow;
}
int n,m;
int fa[maxn];
I int getf(int x){return x==fa[x]?x:fa[x]=getf(fa[x]);}
I void output(int x){
    write(x),pc(' ');
    for(int ee=head[x],v;v=e[ee].v,ee;ee=e[ee].n)
        if(ee>=4*n+2)
            if(e[ee^1].f!=0&&v>n)
                output(v-n);
    return;
}
signed main(){
    int x,y;
    read(n),read(m);
    s=0,t=2*n+1,tot=2*n+2;
    FOR(i,1,n)
        add(s,i,1),
        add(i+n,t,1);
    // cnt = 4n+1; 
    FOR(i,1,m)
        read(x),read(y),
        add(x,y+n,1);
    // cnt = 4n + 2m + 1;
    int ans = n - isap();
    FOR(i,1,n)fa[i]=i;
    for(int ee=4*n+2;ee<=cnt;ee+=2)
        if(e[ee^1].f!=0)
            fa[getf(e[ee].v-n)]=getf(e[ee].u);
    FOR(i,1,n)
        if(getf(i)==i)
            output(i),pc('\n');
    write(ans);
    flush();
    return 0;
}

P3254 圆桌问题

仍然是一个类似二分图的问题,也要输出方案。

我们考虑从S向每个单位连接流量为sis_i的边,表示这个单位的人数,从每个餐桌向T连接流量为cic_i的边,表示餐桌可以容纳的人数,对于所有的单位向所有的餐桌连一条流量为1的边,表示同一个单位来的代表不在同一个餐桌就餐,建图完成。输出方案与上题同理。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <cmath>
#include <vector>
using namespace std;
#define I inline
#define R register
#define inf 1073742823
#define FOR(i,begin,end) for(R int i=begin;i<=end;++i)
#define ROF(i,begin,end) for(R int i=begin;i>=end;--i)
#include<queue>
namespace IO{
    char buf[1<<21],*pa=buf,*pb=buf;
    char buffer[1<<21];int p1=-1;const int p2=(1<<21)-1; 
    I char gc(){return pa==pb&&(pb=(pa=buf)+fread(buf,1,1<<21,stdin),pa==pb)?EOF:*pa++;}
    template<class T>I void read(T &x){
        x=0;R int y=0;R char ch=gc();
        for(;!isdigit(ch);ch=gc())y=ch=='-';
        for(;isdigit(ch);ch=gc())x=x*10+(ch^48);
        (y)&&(x=-x);return;}
    I void flush(){fwrite(buffer,1,p1+1,stdout);p1=-1;return;}
    I void pc(int ch){if(p1==p2)flush();buffer[++p1]=ch;return;}
    template<class T>I void write(T x){
        static char buf[20];static int len =-1;
        if(x>=0){do buf[++len]=(x%10)^48,x/=10;while(x);}
        else{pc('-');do buf[++len]=(-(x%10))^48,x/=10;while(x);}
        while(len>=0)pc(buf[len--]);
        return;}
}
using namespace IO;
int s,t,tot;
const int maxn = 50000+50;
const int maxm = 200000+200;
#define ci const int &
struct edge{
    int u,v,f,n;
    I edge(ci uu =0,ci vv=0,ci ff=0,ci nn=0):u(uu),v(vv),f(ff),n(nn){}
}e[maxm];
int cnt=1,head[maxn],cur[maxn],gap[maxn],dis[maxn],mxflow=0;
I void add(ci u,ci v,ci f){
    e[++cnt]=edge(u,v,f,head[u]),head[u]=cnt;
    e[++cnt]=edge(v,u,0,head[v]),head[v]=cnt;
}
I void bfs(){
    queue<int>q;q.push(t),gap[dis[t]=1]++;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int ee=head[u],v;v=e[ee].v,ee;ee=e[ee].n)
        if(!dis[v])gap[dis[v]=dis[u]+1]++,q.push(v);
    }
}
int dfs(ci u,ci flow){
    if(u==t&&((mxflow+=flow)||1))return flow;
    int us=0,f;
    for(int ee=cur[u],v;cur[u]=ee,v=e[ee].v,ee;ee=e[ee].n)
        if(dis[v]==dis[u]-1){
            if(f=dfs(v,min(flow-us,e[ee].f)))
                e[ee].f-=f,e[ee^1].f+=f,us+=f;
            if(us==flow)return flow;
        }
    (--gap[dis[u]])?(++gap[++dis[u]]):dis[s]=tot+1;
    return us;
}
int n,m,x,sum;
int rr[maxn],cc[maxn];
vector<int>ans[maxn];
signed main(){
    read(n),read(m);
    s=0,t=n+m+1;
    tot=t;
    FOR(i,1,n)
        read(rr[i]),sum+=rr[i],
        add(s,i,rr[i]);
    FOR(i,1,m)
        read(cc[i]),
        add(i+n,t,cc[i]);
    FOR(i,1,n)
        FOR(j,1,m)
            add(i,j+n,1);
    while(dis[s]<=tot+1)memcpy(cur,head,sizeof(head)),dfs(s,inf);
    if(mxflow!=sum){
        puts("0");
        return 0;
    }
    write(1),pc('\n');
    for(int ee=2*(n+m+1);ee<=cnt;ee+=2)
        if(e[ee^1].f!=0)
            ans[e[ee].u].push_back(e[ee].v);
    FOR(i,1,n){
        for(auto k:ans[i])
            write(k-n),pc(' ');
        pc('\n');
    }
    flush();
    return 0;
}

P2764 最小路径覆盖问题

题目渐渐有了难度,模板题洛谷也给了提示

我们考虑拆点,把一个点拆成两个点,第一个拆点表示出边,第二个拆点表示入边,我们把S向第一个拆点连一条流量为1的边,表示这个点的出度最大为1,把第二个拆点向T连接一条流量为1的边,表示这个点的最大入度为1,然后我们连接原图中的边,流量为1,表示存在路径,建图完成。

我们对原图跑一个最大流,显然就是匹配数,所以n-匹配数即为答案,关于路径输出和路径起点,与第一题同理。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <cmath>
using namespace std;
#define I inline
#define R register
#define inf 1073742823
#define FOR(i,begin,end) for(R int i=begin;i<=end;++i)
#define ROF(i,begin,end) for(R int i=begin;i>=end;--i)
#include<queue>
namespace IO{
    char buf[1<<21],*pa=buf,*pb=buf;
    char buffer[1<<21];int p1=-1;const int p2=(1<<21)-1; 
    I char gc(){return pa==pb&&(pb=(pa=buf)+fread(buf,1,1<<21,stdin),pa==pb)?EOF:*pa++;}
    template<class T>I void read(T &x){
        x=0;R int y=0;R char ch=gc();
        for(;!isdigit(ch);ch=gc())y=ch=='-';
        for(;isdigit(ch);ch=gc())x=x*10+(ch^48);
        (y)&&(x=-x);return;}
    I void flush(){fwrite(buffer,1,p1+1,stdout);p1=-1;return;}
    I void pc(int ch){if(p1==p2)flush();buffer[++p1]=ch;return;}
    template<class T>I void write(T x){
        static char buf[20];static int len =-1;
        if(x>=0){do buf[++len]=(x%10)^48,x/=10;while(x);}
        else{pc('-');do buf[++len]=(-(x%10))^48,x/=10;while(x);}
        while(len>=0)pc(buf[len--]);
        return;}
}
using namespace IO;
int tot,s,t;
const int maxn = 50000+50;
const int maxm = 200000+200;
#define ci const int &
struct edge{
    int u,v,f,n;
    I edge(ci uu =0,ci vv=0,ci ff=0,ci nn=0):u(uu),v(vv),f(ff),n(nn){}
}e[maxm];
int cnt=1,head[maxn],cur[maxn],gap[maxn],dis[maxn],mxflow=0;
I void add(ci u,ci v,ci f){
    e[++cnt]=edge(u,v,f,head[u]),head[u]=cnt;
    e[++cnt]=edge(v,u,0,head[v]),head[v]=cnt;
}
I void bfs(){
    queue<int>q;q.push(t),gap[dis[t]=1]++;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int ee=head[u],v;v=e[ee].v,ee;ee=e[ee].n)
        if(!dis[v])gap[dis[v]=dis[u]+1]++,q.push(v);
    }
}
int dfs(ci u,ci flow){
    if(u==t&&((mxflow+=flow)||1))return flow;
    int us=0,f;
    for(int ee=cur[u],v;cur[u]=ee,v=e[ee].v,ee;ee=e[ee].n)
        if(dis[v]==dis[u]-1){
            if(f=dfs(v,min(flow-us,e[ee].f)))
                e[ee].f-=f,e[ee^1].f+=f,us+=f;
            if(us==flow)return flow;
        }
    (--gap[dis[u]])?(++gap[++dis[u]]):dis[s]=tot+1;
    return us;
}
I int isap(){
    bfs();
    while(dis[s]<=tot+1)
        memcpy(cur,head,sizeof head),
        dfs(s,inf);
    return mxflow;
}
int n,m;
int fa[maxn];
I int getf(int x){return x==fa[x]?x:fa[x]=getf(fa[x]);}
I void output(int x){
    write(x),pc(' ');
    for(int ee=head[x],v;v=e[ee].v,ee;ee=e[ee].n)
        if(ee>=4*n+2)
            if(e[ee^1].f!=0&&v>n)
                output(v-n);
    return;
}
signed main(){
    int x,y;
    read(n),read(m);
    s=0,t=2*n+1,tot=2*n+2;
    FOR(i,1,n)
        add(s,i,1),
        add(i+n,t,1);
    // cnt = 4n+1; 
    FOR(i,1,m)
        read(x),read(y),
        add(x,y+n,1);
    // cnt = 4n + 2m + 1;
    int ans = n - isap();
    FOR(i,1,n)fa[i]=i;
    for(int ee=4*n+2;ee<=cnt;ee+=2)
        if(e[ee^1].f!=0)
            fa[getf(e[ee].v-n)]=getf(e[ee].u);
    FOR(i,1,n)
        if(getf(i)==i)
            output(i),pc('\n');
    write(ans);
    flush();
    return 0;
}

P2765 魔术球问题

显然这是一个需要不断加边的网络流,我们将一个数向与他组成完全平方数的数的拆点连边,S向原数连边,拆点向T连边,跑网络流即可,与上一题思想相似。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <cmath>
using namespace std;
#define I inline
#define R register
#define inf 1073742823
#define FOR(i,begin,end) for(R int i=begin;i<=end;++i)
#define ROF(i,begin,end) for(R int i=begin;i>=end;--i)

namespace IO{
    char buf[1<<21],*pa=buf,*pb=buf;
    char buffer[1<<21];int p1=-1;const int p2=(1<<21)-1; 
    I char gc(){return pa==pb&&(pb=(pa=buf)+fread(buf,1,1<<21,stdin),pa==pb)?EOF:*pa++;}
    template<class T>I void read(T &x){
        x=0;R int y=0;R char ch=gc();
        for(;!isdigit(ch);ch=gc())y=ch=='-';
        for(;isdigit(ch);ch=gc())x=x*10+(ch^48);
        (y)&&(x=-x);return;}
    I void flush(){fwrite(buffer,1,p1+1,stdout);p1=-1;return;}
    I void pc(int ch){if(p1==p2)flush();buffer[++p1]=ch;return;}
    template<class T>I void write(T x){
        static char buf[20];static int len =-1;
        if(x>=0){do buf[++len]=(x%10)^48,x/=10;while(x);}
        else{pc('-');do buf[++len]=(-(x%10))^48,x/=10;while(x);}
        while(len>=0)pc(buf[len--]);
        return;}
}
using namespace IO;

const int maxn = 50000+50;
const int maxm = 200000+200;
#define Ci const int &

int n,m,s,t;

struct edge{
    int u,v,n,flow;
    I edge(Ci _u=0,Ci _v=0,Ci _n=0,Ci _flow=0):u(_u),v(_v),n(_n),flow(_flow){}
}e[maxm];
int cnt=1,head[maxn],cur[maxn];
I void add(Ci u,Ci v,Ci flow){
    e[++cnt]=edge(u,v,head[u],flow),head[u]=cnt;
    e[++cnt]=edge(v,u,head[v],0),head[v]=cnt;
}

int dep[maxn],pre[maxn];
#include<queue>
I bool bfs(){
    queue<int>q;
    memset(dep,0,sizeof dep);
    memcpy(cur,head,sizeof(head));
    dep[s]=1,q.push(s);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int ee=head[u],v;v=e[ee].v,ee;ee=e[ee].n)
            if(e[ee].flow&&dep[v]==0)
                dep[v]=dep[u]+1,q.push(v);
    }
    return dep[t];
}
int dfs(int u,int limit){
    if(limit==0||u==t)return limit;
    int flow=0,f;
    for(int ee=cur[u],v;cur[u]=ee,v=e[ee].v,ee;ee=e[ee].n)
        if(dep[v]==dep[u]+1&&(f=dfs(v,min(limit,e[ee].flow)))){
            flow+=f;
            limit-=f;
            e[ee].flow-=f;
            e[ee^1].flow+=f;
            pre[u>>1]=v>>1;
            if(!limit)break;
        }
    return flow;
}
I int dinic(){
    int ans=0;
    while(bfs())
            ans+=dfs(s,inf);
        return ans;
}
I bool check(int x){
    int xx=sqrt(x);
    return xx==x;
}
int top,now,sta[maxn],vis[maxn];
signed main(){
    read(n);
    s=maxn-10,t=maxn-9;
    while(top<=n){
        ++now;
        add(s,now<<1,1);
        add((now<<1)|1,t,1);
        for(int i=sqrt(now)+1;i*i<(now<<1);++i)
            add((i*i-now)<<1,(now << 1)|1,1);
        int flow=dinic();
        if(!flow)
            sta[++top]=now;
    }
    write(now-1),pc('\n');
    FOR(i,1,n)
        if(!vis[sta[i]]){ 
            for(int u=sta[i];u&&u!=(t>>1);u=pre[u])
                vis[u]=1,write(u),pc(' ');
            pc('\n');
        }
    flush();
    return 0;
}

P2766 最长不下降子序列问题

第一问我们考虑直接DP;
第二问我们向原点和拆点连一条为1的边表示只能被选一次,然后对于DP值等于1的从源点S向其连边,流量为1,对于DP等于长度的,从其向汇点T连边,流量为1,表示路径可行性。
第三问在残量网络上加源点到1的边,假如最后一个数在最长上升子序列上,连接一条向汇点的边,还有原点向拆点连边,流量为inf,表示可以无限使用。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <cmath>
using namespace std;
#define I inline
#define R register
#define inf 1073742823
#define FOR(i,begin,end) for(R int i=begin;i<=end;++i)
#define ROF(i,begin,end) for(R int i=begin;i>=end;--i)
#include<queue>
namespace IO{
    char buf[1<<21],*pa=buf,*pb=buf;
    char buffer[1<<21];int p1=-1;const int p2=(1<<21)-1; 
    I char gc(){return pa==pb&&(pb=(pa=buf)+fread(buf,1,1<<21,stdin),pa==pb)?EOF:*pa++;}
    template<class T>I void read(T &x){
        x=0;R int y=0;R char ch=gc();
        for(;!isdigit(ch);ch=gc())y=ch=='-';
        for(;isdigit(ch);ch=gc())x=x*10+(ch^48);
        (y)&&(x=-x);return;}
    I void flush(){fwrite(buffer,1,p1+1,stdout);p1=-1;return;}
    I void pc(int ch){if(p1==p2)flush();buffer[++p1]=ch;return;}
    template<class T>I void write(T x){
        static char buf[20];static int len =-1;
        if(x>=0){do buf[++len]=(x%10)^48,x/=10;while(x);}
        else{pc('-');do buf[++len]=(-(x%10))^48,x/=10;while(x);}
        while(len>=0)pc(buf[len--]);
        return;}
}
using namespace IO;
int tot,s,t;
const int maxn = 5000+50;
const int maxm = 200000+200;
#define ci const int &
struct edge{
    int u,v,f,n;
    I edge(ci uu =0,ci vv=0,ci ff=0,ci nn=0):u(uu),v(vv),f(ff),n(nn){}
}e[maxm];
int cnt=1,head[maxn],cur[maxn],gap[maxn],dis[maxn];
I void add(ci u,ci v,ci f){
    e[++cnt]=edge(u,v,f,head[u]),head[u]=cnt;
    e[++cnt]=edge(v,u,0,head[v]),head[v]=cnt;
}
I void bfs(){
    memset(dis,0,sizeof dis);
    memset(gap,0,sizeof gap);
    queue<int>q;q.push(t),gap[dis[t]=1]++;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int ee=head[u],v;v=e[ee].v,ee;ee=e[ee].n)
        if(!dis[v])gap[dis[v]=dis[u]+1]++,q.push(v);
    }
}
int dfs(ci u,ci flow){
    if(u==t)return flow;
    int us=0,f;
    for(int ee=cur[u],v;cur[u]=ee,v=e[ee].v,ee;ee=e[ee].n)
        if(dis[v]==dis[u]-1){
            if(f=dfs(v,min(flow-us,e[ee].f)))
                e[ee].f-=f,e[ee^1].f+=f,us+=f;
            if(us==flow)return flow;
        }
    (--gap[dis[u]])?(++gap[++dis[u]]):dis[s]=tot+1;
    return us;
}
I int isap(){
    int ans=0;
    bfs();
    while(dis[s]<=tot+1)
        memcpy(cur,head,sizeof head),
        ans+=dfs(s,inf);
    return ans;
}
int n,a[maxn],dp[maxn];
#define ls(i) (i<<1)
#define rs(i) (i<<1|1)
signed main(){
    read(n);
    FOR(i,1,n)read(a[i]),dp[i]=1;
    FOR(i,1,n)
        FOR(j,1,i-1)
            if(a[j]<=a[i])dp[i]=max(dp[i],dp[j]+1);
    int len;FOR(i,1,n)len=max(len,dp[i]);
    write(len),pc('\n');
    s=0,t=1;tot=2+2*n;
    FOR(i,1,n)if(dp[i]==1)add(s,ls(i),1);
    FOR(i,1,n)if(dp[i]==len)add(rs(i),t,1);
    FOR(i,1,n)add(ls(i),rs(i),1);
    FOR(i,1,n)
        FOR(j,1,i-1)
            if(a[j]<=a[i]&&dp[j]+1==dp[i])
                add(rs(j),ls(i),1);
    int last;
    write(last=isap()),pc('\n');
    add(ls(1),rs(1),inf),add(s,ls(1),inf);
    if(dp[n]==len)add(ls(n),rs(n),inf),add(rs(n),t,inf);
    write(last+isap()),pc('\n');
    flush();
    return 0;
}

P2762 太空飞行计划问题

最大权闭合图问题,考虑如下建图方案,由源点S向每个实验仪器连边,流量为费用,由实验向汇点T连边,流量为获利,由实验仪器向对应的实验连边,流量为inf,对所建图求最小割,即最小割为ans,则最后答案为总实验的获利-ans;
(若割边是源点到仪器则表示仪器不选用,若割边为实验则代表实验不选用)

P1251 餐巾计划问题

(之前写的忘了保存挂了,我哭了,嘤嘤嘤)
我们考虑如下建图方式
将每一天拆为两个点,分别表示旧餐巾和新餐巾。
(消耗=产生=r)(不特别说明,费用为零,流量为inf)
由源点向每天的旧餐巾连接一条流量为产生的边,表示每天产生的旧餐巾。
由每天的新餐巾向汇点连接一条流量为消耗的边,表示每天消耗的新餐巾。
由源点向每天的新餐巾连接一条费用的p的边,表示每天购买的新餐巾。
由每天的旧餐巾向时间加m/n处的新餐巾连接一条费用为f/s的边,表示快/慢洗的餐巾。
这样建图我们保证了,最大流一定为每天消耗的新餐巾的总和,最小费用即为最小花费。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <cmath>
#include <queue>
#define int long long
using namespace std;
#define I inline
#define R register
#define inf 2147483647
#define FOR(i,begin,end) for(R int i=begin;i<=end;++i)
#define ROF(i,begin,end) for(R int i=begin;i>=end;--i)

namespace IO{
    char buf[1<<21],*pa=buf,*pb=buf;
    char buffer[1<<21];int p1=-1;const int p2=(1<<21)-1; 
    I char gc(){return pa==pb&&(pb=(pa=buf)+fread(buf,1,1<<21,stdin),pa==pb)?EOF:*pa++;}
    template<class T>I void read(T &x){
        x=0;R int y=0;R char ch=gc();
        for(;!isdigit(ch);ch=gc())y=ch=='-';
        for(;isdigit(ch);ch=gc())x=x*10+(ch^48);
        (y)&&(x=-x);return;}
    I void flush(){fwrite(buffer,1,p1+1,stdout);p1=-1;return;}
    I void pc(int ch){if(p1==p2)flush();buffer[++p1]=ch;return;}
    template<class T>I void write(T x){
        static char buf[20];static int len =-1;
        if(x>=0){do buf[++len]=(x%10)^48,x/=10;while(x);}
        else{pc('-');do buf[++len]=(-(x%10))^48,x/=10;while(x);}
        while(len>=0)pc(buf[len--]);
        return;}
}
using namespace IO;

const int maxn = 50000+500;
const int maxm = 200000+2000;
#define ci const int &

struct edge{
    int u,v,f,w,n;
    I edge(ci uu=0,ci vv=0,ci ff=0,ci ww=0,ci nn=0):u(uu),v(vv),f(ff),w(ww),n(nn){}
}e[maxm];
int dis[maxn],vis[maxn],cur[maxn],head[maxn],cnt=1,n,m,s,t;
int cost=0,flow=0;
I void add(ci u,ci v,ci f,ci w){
    e[++cnt]=edge(u,v,f,w,head[u]),head[u]=cnt;
    e[++cnt]=edge(v,u,0,-w,head[v]),head[v]=cnt;
}
I bool spfa(ci s,ci t){
    memset(vis,0,sizeof vis);
    memset(dis,0x3f,sizeof dis);
    deque<int>q;q.push_front(t);
    vis[t]=1,dis[t]=0;
    while(!q.empty()){
        int u=q.front();q.pop_front();
        for(int ee=head[u],v;v=e[ee].v,ee;ee=e[ee].n)
            if(e[ee^1].f>0&&dis[v]>dis[u]-e[ee].w){
                dis[v]=dis[u]-e[ee].w;
                if(!vis[v])vis[v]=1,(!q.empty()&&dis[q.front()]>dis[v])?
                q.push_front(v):
                q.push_back(v);
            }
        vis[u]=0;
    }
    return dis[s]==0x3f3f3f3f3f3f3f3f?0:1;
}
int dfs(ci u,ci ff){
    if(u==t)return vis[t]=1,ff;
    int us=0,f;vis[u]=1;
    for(int ee=cur[u],v;cur[u]=ee,v=e[ee].v,ee;ee=e[ee].n)
        if(vis[v]==0&&e[ee].f>0&&dis[u]-e[ee].w==dis[v]){
            if((f=dfs(v,min(e[ee].f,ff-us)))>0)
                cost+=f*e[ee].w,e[ee].f-=f,e[ee^1].f+=f,us+=f;
            if(us==ff)break;
        }
    return us;
}
I void cost_flow(){
    while(spfa(s,t)){
        vis[t]=1;
        while(vis[t]){
            memcpy(cur,head,sizeof head);
            memset(vis,0,sizeof vis);
            flow+=dfs(s,inf);
        }
    }
}
int r[maxn];
#define ls(o) (o<<1)
#define rs(o) (o<<1|1)
signed main(){
    read(n);
    int p,t1,c1,t2,c2;
    s=rs(0),t=ls(n+2);
    FOR(i,1,n)
        read(r[i]),add(s,ls(i),r[i],0),add(rs(i),t,r[i],0);
    read(p),read(t1),read(c1),read(t2),read(c2);
    FOR(i,1,n){
        add(s,rs(i),inf,p);
        if(i+1<=n)add(ls(i),ls(i+1),inf,0);
        if(i+t1<=n)add(ls(i),rs(i+t1),inf,c1);
        if(i+t2<=n)add(ls(i),rs(i+t2),inf,c2);
    }
    cost_flow();
    write(cost);
    flush();
    return 0;
}

###P4013 数字梯形问题

我们考虑拆点控制流量,然后就没了。
还有无法在残量网络上继续加边跑最小费用。
洛谷好像不给数据,然后手写了一个数据生成。

第一问:

我们把拆点和原点连接一条流量为1(表次数限制),费用为-点值(表示费用),然后按照题意连边即可。

第二问:

将第一问拆点和原点的连边流量改为inf(表无次数限制)。
同时需要注意最下边一行的点,连接汇点T的边流量也要改为inf。

第三问:

除了源点向第一行的点的连边流量为1外,其他的流量为inf(表无次数限制)

#include <bits/stdc++.h>
using namespace std;
#define I inline
#define R register
#define inf 1073742823
#define FOR(i,begin,end) for(R int i=begin;i<=end;++i)
#define ROF(i,begin,end) for(R int i=begin;i>=end;--i)
namespace IO{
	char buf[1<<21],*pa=buf,*pb=buf;
	char buffer[1<<21];int p1=-1;const int p2=(1<<21)-1; 
	I char gc(){return pa==pb&&(pb=(pa=buf)+fread(buf,1,1<<21,stdin),pa==pb)?EOF:*pa++;}
	template<class T>I void read(T &x){
		x=0;R int y=0;R char ch=gc();
		for(;!isdigit(ch);ch=gc())y=ch=='-';
		for(;isdigit(ch);ch=gc())x=(x<<3)+(x<<1)+(ch^48);
		(y)&&(x=-x);return;}
	I void flush(){fwrite(buffer,1,p1+1,stdout);p1=-1;return;}
	I void pc(int ch){if(p1==p2)flush();buffer[++p1]=ch;return;}
	template<class T>I void write(T x){
		static char buf[20];static int len =-1;
		if(x>=0){do buf[++len]=(x%10)^48,x/=10;while(x);}
		else{pc('-');do buf[++len]=(-(x%10))^48,x/=10;while(x);}
		while(len>=0)pc(buf[len--]);
		return;}
}
using namespace IO;
int main(){
	srand(GetTickCount());
	int n=10,m=2;
	write(m),pc(' '),write(n),pc('\n');
	FOR(i,m,m+n-1)
	{
        FOR(j,1,i)write(rand()%100),pc(' ');
		pc('\n');
	}
	flush();
	return 0;
}

题目的代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <cmath>
#include <queue>
using namespace std;
#define I inline
#define R register
#define inf 1073742823
#define FOR(i,begin,end) for(R int i=begin;i<=end;++i)
#define ROF(i,begin,end) for(R int i=begin;i>=end;--i)

namespace IO{
    char buf[1<<21],*pa=buf,*pb=buf;
    char buffer[1<<21];int p1=-1;const int p2=(1<<21)-1; 
    I char gc(){return pa==pb&&(pb=(pa=buf)+fread(buf,1,1<<21,stdin),pa==pb)?EOF:*pa++;}
    template<class T>I void read(T &x){
        x=0;R int y=0;R char ch=gc();
        for(;!isdigit(ch);ch=gc())y=ch=='-';
        for(;isdigit(ch);ch=gc())x=x*10+(ch^48);
        (y)&&(x=-x);return;}
    I void flush(){fwrite(buffer,1,p1+1,stdout);p1=-1;return;}
    I void pc(int ch){if(p1==p2)flush();buffer[++p1]=ch;return;}
    template<class T>I void write(T x){
        static char buf[20];static int len =-1;
        if(x>=0){do buf[++len]=(x%10)^48,x/=10;while(x);}
        else{pc('-');do buf[++len]=(-(x%10))^48,x/=10;while(x);}
        while(len>=0)pc(buf[len--]);
        return;}
}
using namespace IO;

const int maxn = 50000+500;
const int maxm = 2000000+2000;
#define ci const int &

struct edge{
    int u,v,f,w,n;
    I edge(ci uu=0,ci vv=0,ci ff=0,ci ww=0,ci nn=0):u(uu),v(vv),f(ff),w(ww),n(nn){}
}e[maxm];
int dis[maxn],vis[maxn],cur[maxn],head[maxn],cnt=1,n,m,s,t;
int cost=0,flow=0;
I void add(ci u,ci v,ci f,ci w){
    e[++cnt]=edge(u,v,f,w,head[u]),head[u]=cnt;
    e[++cnt]=edge(v,u,0,-w,head[v]),head[v]=cnt;
}
I bool spfa(ci s,ci t){
    memset(vis,0,sizeof vis);
    memset(dis,0x3f,sizeof dis);
    deque<int>q;q.push_front(t);
    vis[t]=1,dis[t]=0;
    while(!q.empty()){
        int u=q.front();q.pop_front();
        for(int ee=head[u],v;v=e[ee].v,ee;ee=e[ee].n)
            if(e[ee^1].f>0&&dis[v]>dis[u]-e[ee].w){
                dis[v]=dis[u]-e[ee].w;
                if(!vis[v])vis[v]=1,(!q.empty()&&dis[q.front()]>dis[v])?
                q.push_front(v):
                q.push_back(v);
            }
        vis[u]=0;
    }
    return dis[s]==0x3f3f3f3f?0:1;
}
int dfs(ci u,ci ff){
    if(u==t)return vis[t]=1,ff;
    int us=0,f;vis[u]=1;
    for(int ee=cur[u],v;cur[u]=ee,v=e[ee].v,ee;ee=e[ee].n)
        if(vis[v]==0&&e[ee].f>0&&dis[u]-e[ee].w==dis[v]){
            if((f=dfs(v,min(e[ee].f,ff-us)))>0)
                cost+=f*e[ee].w,e[ee].f-=f,e[ee^1].f+=f,us+=f;
            if(us==ff)break;
        }
    return us;
}
I void cost_flow(){
    while(spfa(s,t)){
        vis[t]=1;
        while(vis[t]){
            memcpy(cur,head,sizeof head);
            memset(vis,0,sizeof vis);
            flow+=dfs(s,inf);
        }
    }
}
I void clear(){
    memset(head,0,sizeof head);
    memset(e,0,sizeof e);
    cnt=1;cost=0,flow=0;
}
#define ls(o) (o<<1)
#define rs(o) (o<<1|1)
int a[50][50],id[50][50];
signed main(){
    s=ls(0),t=rs(0);
    int tot=0;
    read(m),read(n);
    FOR(i,m,m+n-1)
        FOR(j,1,i)
            read(a[i][j]),id[i][j]=++tot;
    // work 1
    FOR(i,1,m)
        add(s,ls(id[m][i]),1,0);
    FOR(i,m,m+n-1)
        FOR(j,1,i)
            add(ls(id[i][j]),rs(id[i][j]),1,-a[i][j]);
    FOR(i,m,m+n-2)
        FOR(j,1,i)
            add(rs(id[i][j]),ls(id[i+1][j]),1,0),
            add(rs(id[i][j]),ls(id[i+1][j+1]),1,0);
    FOR(i,1,m+n-1)
        add(rs(id[m+n-1][i]),t,1,0);
    cost_flow();
    write(-cost),pc('\n');
    // work 2
    clear();
    FOR(i,1,m)
        add(s,ls(id[m][i]),1,0);
    FOR(i,m,m+n-1)
        FOR(j,1,i)
            add(ls(id[i][j]),rs(id[i][j]),inf,-a[i][j]);
    FOR(i,m,m+n-2)
        FOR(j,1,i)
            add(rs(id[i][j]),ls(id[i+1][j]),1,0),
            add(rs(id[i][j]),ls(id[i+1][j+1]),1,0);
    FOR(i,1,m+n-1)
        add(rs(id[m+n-1][i]),t,1,0);
    cost_flow();
    write(-cost),pc('\n');
    // work 3
    clear();
    FOR(i,1,m)
        add(s,ls(id[m][i]),1,0);
    FOR(i,m,m+n-1)
        FOR(j,1,i)
            add(ls(id[i][j]),rs(id[i][j]),inf,-a[i][j]);
    FOR(i,m,m+n-2)
        FOR(j,1,i)
            add(rs(id[i][j]),ls(id[i+1][j]),inf,0),
            add(rs(id[i][j]),ls(id[i+1][j+1]),inf,0);
    FOR(i,1,m+n-1)
        add(rs(id[m+n-1][i]),t,inf,0);
    cost_flow();
    write(-cost),pc('\n');
    flush();
    return 0;
}

大概本博客就完结了,网络流二十四题的题目连接直接放到下面了:

P1251 餐巾计划问题
P2754 [CTSC1999]家园 / 星际转移问题
P2756 飞行员配对方案问题
P2761 软件补丁问题
P2762 太空飞行计划问题
P2763 试题库问题
P2764 最小路径覆盖问题
P2765 魔术球问题
P2766 最长不下降子序列问题
P2770 航空路线问题
P2774 方格取数问题
P2775 机器人路径规划问题
P3254 圆桌问题
P3355 骑士共存问题
P3356 火星探险问题
[P3357 最长k可重线段集问题]https://www.luogu.com.cn/problem/P3357)
P3358 最长k可重区间集问题
P4009 汽车加油行驶问题
P4011 孤岛营救问题
P4012 深海机器人问题
P4013 数字梯形问题
P4014 分配问题
P4015 运输问题
P4016 负载平衡问题

##当然网络流24题只是比较基础的入门题目,本博客只提供入门方面的参考