网络流

最大流

由于其他算法并不常用,所以这里只给出 Dinic\text{Dinic} 的用法。

Dinic

常量与变量

  • const ll INF\texttt{const ll INF}:极大值。
  • int head[N],ver[M<<1],nxt[M<<1],tot\texttt{int head[N],ver[M<<1],nxt[M<<1],tot}:图的信息。
  • ll edge[M<<1]\texttt{ll edge[M<<1]}:边权。
  • ll maxflow\texttt{ll maxflow}:最大流。
  • int S\texttt{int S}:源点。
  • int T\texttt{int T}:汇点。
  • int d[i]\texttt{int d[i]}:点 ii 的层数。
  • int now[i]\texttt{int now[i]}:点 ii 的当前弧。
  • queue<int>q\texttt{queue<int>q}bfs\text{bfs} 时的队列。

函数

  • void add(int x,int y,ll z)\texttt{void add(int x,int y,ll z)}:添加边 x,yx,y,边权为 zz
  • bool bfs()\texttt{bool bfs()}:进行广搜看是否有增广路。
  • ll dinic(int x,ll flow)\texttt{ll dinic(int x,ll flow)}:求增广路能贡献的流。
  • void solve()\texttt{void solve()}:求最大流。

代码

struct Dinic{
	const ll INF=0x3f3f3f3f3f3f3f3f;
	int head[N],ver[M<<1],nxt[M<<1],tot=1;
	ll edge[M<<1];
	void add(int x,int y,ll z){
		ver[++tot]=y,edge[tot]=z,nxt[tot]=head[x],head[x]=tot;
		ver[++tot]=x,edge[tot]=0,nxt[tot]=head[y],head[y]=tot;
	}
	ll maxflow;
	int S,T,d[N],now[N];
	queue<int>q;
	bool bfs(){
		memset(d,0,sizeof(d));
		while(q.size())q.pop();
		q.push(S);d[S]=1;now[S]=head[S];
		while(q.size()){
			int x=q.front();q.pop();
			for(int i=head[x],y;i;i=nxt[i]){
				y=ver[i];
				if(edge[i]&&!d[y]){
					q.push(y);now[y]=head[y];
					d[y]=d[x]+1;
					if(y==T)return 1;
				}
			}
		}
		return 0;
	}
	ll dinic(int x,ll flow){
		if(x==T)return flow;
		ll rest=flow;
		for(int i=now[x],k,y;i&&rest;i=nxt[i]){
			y=ver[i];now[x]=i;
			if(edge[i]&&d[y]==d[x]+1){
				k=dinic(y,min(rest,edge[i]));
				if(!k)d[y]=0;
				edge[i]-=k;edge[i^1]+=k;
				rest-=k;
			}
		}
		return flow-rest;
	}
	void solve(){
		ll flow=maxflow=0;
		while(bfs())while(flow=dinic(S,INF))maxflow+=flow;
	}
}mf;

Edmonds-Karp 动能算法

代码

const ll INF=1ll<<60;
int n,m,s,t,pre[N];
int head[N],nxt[M<<1],ver[M<<1],tot=1;
ll incf[N],edge[M<<1],maxflow=0;
bool v[N];
void add(int x,int y,ll z){
  ver[++tot]=y,edge[tot]=z,nxt[tot]=head[x],head[x]=tot;
  ver[++tot]=x,edge[tot]=0,nxt[tot]=head[y],head[y]=tot;
}
bool fk_bfs(){
  memset(v,0,sizeof(v));
  queue<int>q;
  q.push(s);
  v[s]=1;
  incf[s]=INF;
  while(q.size()){
  	int x=q.front();
  	q.pop();
  	for(int i=head[x];i;i=nxt[i])
  		if(edge[i]){
  			int y=ver[i];
  			if(v[y])
  				continue;
  			incf[y]=min(incf[x],edge[i]);
  			pre[y]=i;
  			v[y]=1;
  			q.push(y);
  			if(y==t)
  				return 1;
  		}
  }
  return 0;
}
void update(){
  int x=t;
  while(x!=s){
  	int i=pre[x];
  	edge[i]-=incf[t];
  	edge[i^1]+=incf[t];
  	x=ver[i^1];
  }
  maxflow+=incf[t];
}
void fk(){
  while(fk_bfs())
  	update();
}

Highest Label Preflow Push 最高标号预流推进算法

代码

int n,m,s,t;
int head[N],nxt[M<<1],ver[M<<1],edge[M<<1],tot=1;
int cnt=1,h[N],rest[N],gap[N],hest=0;
stack<int>B[N];
void add(int x,int y,ll z){
  ver[++tot]=y,edge[tot]=z,nxt[tot]=head[x],head[x]=tot;
  ver[++tot]=x,edge[tot]=0,nxt[tot]=head[y],head[y]=tot;
}
int push(int x){
  for(int i=head[x];i;i=nxt[i]){
  	int y=ver[i],z=edge[i];
  	if(z&&(x==s||h[x]==h[y]+1)){
  		int k=(x==s)?z:min(z,rest[x]);
  		if(y!=s&&y!=t&&!rest[y]){
  			B[h[y]].push(y);
  			hest=max(hest,h[y]);
  		}
  		rest[x]-=k;
  		rest[y]+=k;
  		edge[i]-=k;
  		edge[i^1]+=k;
  		if(!rest[x])
  			return 0;
  	}
  }
  return 1;
}
void relabel(int x){
  h[x]=h[0];
  for(int i=head[x];i;i=nxt[i])
  	if(edge[i])
  		h[x]=min(h[x],h[ver[i]]);
  if(++h[x]<n){
  	B[h[x]].push(x);
  	hest=max(hest,h[x]);
  	gap[h[x]]++;
  }
}
bool bfs(){
  memset(h,0x3f,sizeof(h));
  queue<int>q;
  q.push(t);
  h[t]=0;
  while(q.size()){
  	int x=q.front();
  	q.pop();
  	for(int i=head[x];i;i=nxt[i])
  		if(edge[i^1]&&h[ver[i]]>h[x]+1){
  			h[ver[i]]=h[x]+1;
  			q.push(ver[i]);
  		}
  }
  return h[s]!=h[0];
}
int select(){
  while(!B[hest].size()&&hest>=0)
  	hest--;
  return hest==-1?0:B[hest].top();
}
int hlpp(){
  if(!bfs())
  	return 0;
  memset(gap,0,sizeof(gap));
  for(int i=1;i<=n;i++)
  	if(h[i]!=h[0])
  		gap[h[i]]++;
  h[s]=n;
  push(s);
  int x;
  while(x=select()){
  	B[hest].pop();
  	if(push(x)){
  		if(!(--gap[h[x]]))
  			for(int i=1;i<=n;i++)
  				if(i!=s&&i!=t&&h[i]>h[x]&&h[i]<n+1)
  					h[i]=n+1;
  		relabel(x);
  	}
  }
  return rest[t];
}

最小费用最大流

SSP 算法

常量与变量

  • const ll INF\texttt{const ll INF}:极大值。
  • ll cost[i]\texttt{ll cost[i]}:边 ii 的费用。
  • ll dis[i]\texttt{ll dis[i]}:点 ii 到源点的最小费用路径的费用。
  • ll mxflow\texttt{ll mxflow}:最大流。
  • ll micost\texttt{ll micost}:最小费用。
  • queue<int>q\texttt{queue<int>q}spfa\text{spfa} 时的队列。
  • int v[i]\texttt{int v[i]}:点 ii 的标记数组。
  • int now[i]\texttt{int now[i]}:点 ii 的弧优化数组。
  • int d[i]\texttt{int d[i]}:点 ii 的层数。
  • int S\texttt{int S}:源点。
  • int T\texttt{int T}:汇点。

函数

  • bool spfa()\texttt{bool spfa()}:看是否有增广路。
  • ll dinic(int x,ll flow)\texttt{ll dinic(int x,ll flow)}:求增广路能贡献的流。
  • void solve()\texttt{void solve()}:求最小费用最大流。

代码

struct Dinic{
	const ll INF=0x3f3f3f3f3f3f3f3f;
	int head[N],ver[M<<1],nxt[M<<1],tot=1;
	ll edge[M<<1],cost[M<<1],dis[N],mxflow,micost;
	void add(int x,int y,ll z,ll w){
		ver[++tot]=y,edge[tot]=z,cost[tot]=w,nxt[tot]=head[x],head[x]=tot;
		ver[++tot]=x,edge[tot]=0,cost[tot]=-w,nxt[tot]=head[y],head[y]=tot;
	}
	queue<int>q;
	int v[N],now[N],d[N],S,T;
	bool spfa(){
		memset(dis,0x3f,sizeof(dis));
		memset(v,0,sizeof(v));
		while(q.size())q.pop();
		q.push(S);dis[S]=0;d[S]=v[S]=1;now[S]=head[S];
		while(q.size()){
			int x=q.front();q.pop();
			v[x]=0;
			for(int i=head[x],y;i;i=nxt[i]){
				y=ver[i];
				if(edge[i]&&dis[y]>dis[x]+cost[i]){
					dis[y]=dis[x]+cost[i];
					d[y]=d[x]+1;
					if(!v[y]){
						q.push(y);
						v[y]=1;now[y]=head[y];
					}
				}
			}
		}
		return dis[T]!=INF;
	}
	ll dinic(int x,ll flow){
		if(x==T)return flow;
		ll rest=flow,k;
		for(int i=now[x],y;i&&rest;i=nxt[i]){
			y=ver[i];now[x]=i;
			if(edge[i]&&dis[y]==dis[x]+cost[i]&&d[y]==d[x]+1){
				k=dinic(y,min(edge[i],rest));
				if(!k)d[y]=0;
				edge[i]-=k;edge[i^1]+=k;
				rest-=k;micost+=k*cost[i];
			}
		}
		return flow-rest;
	}
	void solve(){
		ll flow=mxflow=micost=0;
		while(spfa())while(flow=dinic(S,INF))mxflow+=flow;
	}
}mf;
posted @ 2023-11-03 21:22  luckydrawbox  阅读(7)  评论(0)    收藏  举报  来源