• 【网络流】


    前置

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

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

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

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

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

    •把整个网络的流量定义为(即从s流出的流量):(|f|=sumlimits_{vin 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<bits/stdc++.h>
    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 <class t>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<int> 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<bits/stdc++.h>
    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 <class t>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];
    queue<int>q;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<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<cmath>
    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 <class t>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<int> 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]<dis[u]+(cos=e[i].cos)){
    				dis[v]=dis[u]+cos;
    				incf[v]=Min(incf[u],flo),pre[v]=i;
    				if(!vis[v]) q.push(v),vis[v]=1;
    			}
    		}
    	}
    	if(dis[t]==0xcfcfcfcf) return 0;
    	return 1;
    }
    
    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],ans+=dis[t]*incf[t];
    }
    
    int main(){
    	freopen("in2.txt","r",stdin);
    	//freopen("xor.out","w",stdout);
    	rd(n),rd(k),tt=n*n;
    	s=1,t=tt<<1,ans=mxflo=0;
    	for(int i=1;i<=n;++i)
    	for(int j=1,val,x,y;j<=n;++j){
    		rd(val);x=adr(i,j),y=x+tt;
    		add(x,y,1,val),add(x,y,k-1,0);
    		if(j<n) add(y,x+1,k,0);
    		if(i<n) add(y,x+n,k,0);
    	}
    	while(spfa())
    	upd();
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    P3916 图的遍历 dfs
    P4568 [JLOI2011]飞行路线 分层图最短路
    P1948 [USACO08JAN]电话线Telephone Lines spfa 二分答案
    P1849 [USACO12MAR]拖拉机Tractor bfs
    P1730 最小密度路径 floyed
    P1661 扩散 二分答案 并查集
    使用unittest和Django搭配写一个接口测试平台
    AJAX解决跨域的几种方式
    Django
    基于pytest框架自动化测试脚本的编写
  • 原文地址:https://www.cnblogs.com/lxyyyy/p/11409600.html
Copyright © 2020-2023  润新知