【网络流】

前置

• 一个流网络(flow network)\(G=(V,E)\)是一个有向图,每个边\((u,v)\in E\)有一个非负容量(capacity) \(c(u,v)>=0\). 对于不在\(E\)中的\((u, v)\), 规定\(c(u, v)=0\)
• 有两个特殊结点: 源(source)\(s(s\in V)\)和汇(sink)\(t(t\in V)\).
• 假设对于任意其他点v, 存在通路\(s\rarr v\rarr t\)

• 流是一个边的函数\(f(u,v)\),流量函数\(f(x,y)\,(x\in V,y\in V)\) 满足:

​ •容量限制:\(\forall(u,v),\;0\le f(u,v)\le c(u,v)\)

​ •对称性:\(f(u,v)=-f(v,u)\)

​ •收支平衡:对于不是\(s\)\(t\)的其他点\(u\),有\(\sum\limits_{v\in V}f(u,v)=0\)

•把整个网络的流量定义为(即从s流出的流量):\(|f|=\sum\limits_{v\in V}f(s,v)\)
• 最大流问题: 寻找流函数\(f\), 使得网络流最大

•其他

等价条件:
1) f是最大流
2)残量网络中无可增广路
3)存在某个切割(S,T), |f|=c(S,T)

每次找边数最少的增广路进行增广
• 定理:最多增广O(nm)次
• Edmonds-Karp:每次BFS找增广路,无需维护附加信息
• Dinic:多次BFS构造层次图,DFS找增广路。附加信息:
层次(起点到u的距离值)

最大流

luogu3376网络最大流【模板】

每次bfs找增广路 直到图中没有增广路

#include
using namespace std;
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)>(y)?(y):(x))
const int N=10000+5,M=100000+5,inf=0x3f3f3f3f,P=19650827;
int n,m,s,t,maxflow=0,pre[N],incf[N];
template void rd(t &x){
    x=0;int w=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=w?-x:x;
}

int head[N],tot=1;
struct edge{int v,w,nxt;}e[M<<1];
void add(int u,int v,int w){
    e[++tot]=(edge){v,w,head[u]},head[u]=tot;
}

bool vis[N];
queue q;
bool bfs(){
    while(!q.empty()) q.pop();
    memset(vis,0,sizeof(vis));
    q.push(s),vis[s]=1,incf[s]=inf;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u],v,w;i;i=e[i].nxt)
        if(w=e[i].w){
            v=e[i].v;
            if(vis[v]) continue;
            incf[v]=Min(incf[u],w),pre[v]=i;
            q.push(v),vis[v]=1;
            if(v==t) return 1;
        }
    }
    return 0;
}

void upd(){
    int x=t;
    while(x!=s){
        int i=pre[x];
        e[i].w-=incf[t],e[i^1].w+=incf[t];
        x=e[i^1].v;
    }
    maxflow+=incf[t];
}

int main(){
    rd(n),rd(m),rd(s),rd(t);
    for(int i=1,u,v,w;i<=m;++i)
    rd(u),rd(v),rd(w),add(u,v,w),add(v,u,0);
    while(bfs())
    upd();
    printf("%d",maxflow);
    return 0;
}

费用流

[luogu3381]【模板】最小费用最大流

#include
using namespace std;
#define ll long long
#define rg register
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)>(y)?(y):(x))
const int N=5000+5,M=50000+5,inf=0x3f3f3f3f,P=19650827;
int n,m,s,t,pre[N],incf[N],mxflo,mncos;
template void rd(t &x){
    x=0;int w=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=w?-x:x;
}

int head[N],tot=1;
struct edge{int v,flo,cos,nxt;}e[M<<1];
void add(int u,int v,int flo,int cos){
    e[++tot]=(edge){v,flo,cos,head[u]},head[u]=tot;
    e[++tot]=(edge){u,0,-cos,head[v]},head[v]=tot;
}

int dis[N];
queueq;bool vis[N];
bool spfa(){
    memset(vis,0,sizeof(vis));
    memset(dis,inf,sizeof(dis));
    q.push(s),vis[s]=1,dis[s]=0,incf[s]=inf;
    while(!q.empty()){
        int u=q.front();q.pop(),vis[u]=0;
        for(int i=head[u],v,flo,cos;i;i=e[i].nxt)
        if((flo=e[i].flo)&&dis[v=e[i].v]>dis[u]+(cos=e[i].cos)){
            dis[v]=dis[u]+cos,pre[v]=i,incf[v]=Min(incf[u],flo);
            if(!vis[v]) q.push(v),vis[v]=1;
        }
    }
    return dis[t]!=inf;
}
void upd(){
    int x=t;
    while(x!=s){
        int i=pre[x];
        e[i].flo-=incf[t],e[i^1].flo+=incf[t];
        x=e[i^1].v;
    }
    mxflo+=incf[t],mncos+=incf[t]*dis[t];
}

int main(){
    freopen("in2.txt","r",stdin);
//  freopen("DNA.out","w",stdout);
    rd(n),rd(m),rd(s),rd(t);
    for(int i=1,u,v,w,z;i<=m;++i)
    rd(u),rd(v),rd(w),rd(z),add(u,v,w,z);
    while(spfa()) upd();
    printf("%d %d",mxflo,mncos);
    return 0;
}

luogu2045方格取数加强版

给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大

最大费用最大流

  • ”点边转化“ 将每个格子\((i,j)\)拆成一个”入点“和一个”出点“
  • 从每个\((i,j)\)的入点向出点连两条有向边 第一条有向边容量为1,费用为格子中的数 第二条容量为\(k-1\),费用为0, 表示第一次经过该点可把数字取走 之后再经过就不再计算
  • \((i,j)\)的出点向\((i,j+1)\)\((i+1,j)\)连有向边 容量为k 费用为0
#include
#include
#include
#include
#include
using namespace std;
#define Min(x,y) ((x)<(y)?(x):(y))
const int N=5000+5,M=150+5,inf=0x3f3f3f3f,P=19650827;
int n,k,tt,s,t,incf[N],pre[N],mxflo,ans;
template void rd(t &x){
    x=0;int w=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=w?-x:x;
}

int head[N],tot=1;
struct edge{int v,flo,cos,nxt;}e[N<<2];
void add(int u,int v,int flo,int cos){
    e[++tot]=(edge){v,flo,cos,head[u]},head[u]=tot;
    e[++tot]=(edge){u,0,-cos,head[v]},head[v]=tot;
}
int adr(int x,int y){return (x-1)*n+y;}

queue q;
bool vis[N];int dis[N];
bool spfa(){
    memset(vis,0,sizeof(vis));
    memset(dis,0xcf,sizeof(dis));
    q.push(s),vis[s]=1,dis[s]=0,incf[s]=1<<30;
    while(!q.empty()){
        int u=q.front();q.pop(),vis[u]=0;
        for(int i=head[u],v,flo,cos;i;i=e[i].nxt){
            if(!(flo=e[i].flo)) continue;
            if(dis[v=e[i].v]

转载于:https://www.cnblogs.com/lxyyyy/p/11409600.html

你可能感兴趣的:(【网络流】)