网络流专题

网络流及建模专题(上) 前言 不断更新中…… 这几天新坑填不下去了,回来回顾一些经典的模型套路,先拿网络流开刀,窃以为洛谷这几道网络流的题目还是非常具有代表性的,涵盖了网络流调整、多解计数、最小割、最大权闭合子图问题。 还涵盖了图论(二分图)中的一些结论和:最小不相交路径覆盖、最小可相交路径覆盖、二分图最大点权独立集、二分图最小点权覆盖集等问题,这里将简略介绍一下。 本专题包含六道题:P2765、P2764、P2... 阅读详情

前情提要

  1. 建议使用 B e l l m a n F o r d BellmanFord BellmanFord 算法跑最短路 . . .

最大流

D i n i c Dinic Dinic 算法

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char InList[7500005];int inpos=7500000;
inline char GetChar()
{
	if(inpos>=7500000)fread(InList,1,7500000,stdin),inpos=0;
	return InList[inpos++];
}
template<typename Typename>
inline void Read(Typename &x)
{
	x=0;
	register char c=GetChar();
	while(c<48||c>57)c=GetChar();
	while(48<=c&&c<=57){x=x*10+c-48;c=GetChar();}
}
template<typename Typename>
inline void Read(Typename &x,Typename&y){
	Read(x);Read(y);
}
template<typename Typename>
inline void Read(Typename &x,Typename&y,Typename&z){
	Read(x,y);Read(z);
}
template<typename Typename>
inline void Read(Typename &a,Typename&b,Typename&c,Typename&d){
	Read(a,b);Read(c,d);
}

template<typename Typename,int MaxCnt>
struct Queues{
	private:
		Typename Data[MaxCnt];
		int head,tail,size;
	public:
		Queues(){
			memset(Data,0,sizeof(Data));
			head=tail=size=0;
		}
		inline void Clear(){
			head=tail=size=0;
		}
		inline void Push(const Typename&x){
			if(size==MaxCnt)puts("Error Queue");
			else{
				size++;
				Data[tail]=x;
				tail++;
				if(tail==MaxCnt)tail=0;
			}
		}
		inline void Pop(){
			if(size==0)puts("Error Queue");
			else{
				size--;
				head++;
				if(head==MaxCnt)head=0;
			}
		}
		inline Typename Front(){
			if(size==0){puts("Error Queue");return Typename();}
			else return Data[head];
		}
		inline bool Empty(){return(size==0);}
		inline int Size(){return size;}
};

template<typename Typename,int MaxN,int MaxM>
struct Graphs{
	int Last[MaxN],cntn;
	Typename Flow[MaxM];
	int To[MaxM],Next[MaxM],cntm;
	Graphs(){
		cntn=cntm=0;
		memset(Last,-1,sizeof(Last));
	}
	inline void Clear(){
		cntn=cntm=0;
		memset(Last,-1,sizeof(Last));
	}
};

template<typename Typename>
inline void Insert(Typename&G,int from,int to,long long flow1,long long flow2){
	G.To[G.cntm]=to;G.Flow[G.cntm]=flow1;
	G.Next[G.cntm]=G.Last[from];G.Last[from]=G.cntm++;
	G.To[G.cntm]=from;G.Flow[G.cntm]=flow2;
	G.Next[G.cntm]=G.Last[to];G.Last[to]=G.cntm++;
}
template<typename Typename>
inline Typename Min(const Typename&x,const Typename&y){
	if(x<y)return x;return y;
}
template<typename Typename>
inline Typename Max(const Typename&x,const Typename&y){
	if(x<y)return x;return y;
}

const long long Inf=0x3f3f3f3f3f3f3f3f;
const int MaxN=210;
const int MaxM=11000;

Graphs<long long,MaxN,MaxM>G;
Queues<int,MaxN>Q;
int st,ed,sizev;
bool Visit[MaxN];
long long High[MaxN];
inline bool Bfs(){
	for(register int i=1;i<=sizev;i++)
		High[i]=0;
	High[ed]=1;Q.Clear();Q.Push(ed);
	while(!Q.Empty()){
		int from=Q.Front();Q.Pop();
		for(register int k=G.Last[from];k!=-1;k=G.Next[k]){
			if(G.Flow[k^1]>0&&High[G.To[k]]==0){
				Q.Push(G.To[k]);
				High[G.To[k]]=High[from]+1;
				if(G.To[k]==st)return true;
			}
		}
	}
	return false;
}
int Cur[MaxN];
inline long long Dfs(int from,long long flow){
	if(from==ed)return flow;
	long long used=0,test;
	for(register int k=Cur[from];k!=-1;k=G.Next[k]){
		if(G.Flow[k]>0&&High[from]-1==High[G.To[k]]){
			used+=(test=Dfs(G.To[k],Min(G.Flow[k],flow-used)));
			G.Flow[k]-=test;G.Flow[k^1]+=test;
			Cur[from]=k;
			if(used==flow){return used;}
		}
	}
	if(used==0)High[from]=0;
	return used;
}
int n,m;
int main(){
	Read(n,m,st,ed);
	sizev=n;
	for(register int i=1;i<=m;i++){
		int x,y;long long f;
		Read(x,y),Read(f);
		Insert(G,x,y,f,0);
	}
	long long ans=0,test;
	while(Bfs()){
		memcpy(Cur+1,G.Last+1,sizeof(int)*n);
		while(test=Dfs(st,Inf))ans+=test;
	}
	printf("%lld",ans);
	return 0;
}

最小费用流

C a p a c i t y − S c a l i n g Capacity-Scaling Capacity−Scaling 算法

F r o m From From O u u a n Ouuan Ouuan

  1. D i j k s t r a Dijkstra Dijkstra 算法版
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

template<typename Typename>
inline Typename Max(const Typename&x,const Typename&y){
	if(x>y)return x;return y;
}
template<typename Typename>
inline Typename Min(const Typename&x,const Typename&y){
	if(x<y)return x;return y;
}
template<typename Typename>
inline void Swap(Typename&x,Typename&y){
	Typename t=x;x=y;y=t;
}

template<typename Typename,int MaxCnt>
struct Heaps{
	private:
		Typename Data[MaxCnt];
		int size;
		inline void UpFix(int now){
			int i=now>>1,j=now;//Dad[j]=i;
			while(i!=0){
				if(Data[i]<Data[j])break;
				Swap(Data[i],Data[j]);
				i>>=1;j>>=1;
			}
		}
		inline void DownFix(int now){
			int i=now,j=now<<1;
			while(j<=size){
				if(j+1<=size&&Data[j+1]<Data[j])j++;
				if(Data[i]<Data[j])break;
				Swap(Data[i],Data[j]);
				i=j;j<<=1;
			}
		}
	public:
		Heaps(){
			memset(Data,0,sizeof(Data));
			size=0;
		}
		inline void Clear(){size=0;}
		inline void Push(const Typename&x){
			if(size==MaxCnt-1)puts("Error Heap");
			else{
				Data[++size]=x;
				UpFix(size);
			}
		}
		inline void Pop(){
			Data[1]=Data[size--];
			DownFix(1);
		}
		inline Typename Top(){
			if(size==0){puts("Error Heap");return Typename();} 
			else return Data[1];
		}
		inline bool Empty(){return size==0;}
		inline int Size(){return size;}
};

template<typename Typename,int MaxN,int MaxM>
struct Graphs{
	int Last[MaxN],cntn;
	Typename Cost[MaxM],Flow[MaxM];
	int To[MaxM],Next[MaxM],cntm;
	Graphs(){
		cntn=cntm=0;
		memset(Last,-1,sizeof(Last));
	}
	inline void Clear(){
		cntn=cntm=0;
		memset(Last,-1,sizeof(Last));
	}
};

template<typename Typename>
inline void Insert(Typename&G,int from,int to,long long flow1,long long flow2,long long cost1,long long cost2){
	G.To[G.cntm]=to;G.Flow[G.cntm]=flow1;G.Cost[G.cntm]=cost1;
	G.Next[G.cntm]=G.Last[from];G.Last[from]=G.cntm++;
	
	G.To[G.cntm]=from;G.Flow[G.cntm]=flow2;G.Cost[G.cntm]=cost2;
	G.Next[G.cntm]=G.Last[to];G.Last[to]=G.cntm++;
}

template<typename Typename1,typename Typename2>
struct Pairs{
	Typename1 x;Typename2 y;
	Pairs(const Typename1 _x=Typename1(),Typename2 _y=Typename2()):
		x(_x),y(_y){}
	inline bool operator<(const Pairs<Typename1,Typename2>p)const{
		return x==p.x?y<p.y:x<p.x;
	}
};

const long long Inf=0x3f3f3f3f3f3f3f3f;
const long long Limit=1e12;
const int MaxN=6e3,MaxM=1e5+1e4;

Graphs<long long,MaxN,MaxM>G;
Heaps<Pairs<long long,int>,MaxM>H;
long long Weight[MaxN],Dist[MaxN],Flow[MaxM];
int Pre[MaxN];

inline long long CalcCost(const int&id){
	return Weight[G.To[id^1]]+G.Cost[id]-Weight[G.To[id]];
}
int sizev;

bool Visit[MaxN];

void Dijkstra(int start)
{
	for(register int i=1;i<=sizev+1;i++)
		Visit[i]=false,Dist[i]=Inf,Pre[i]=-1;
    Dist[start]=0;H.Push(Pairs<long long,int>(0ll,start));
    while(!H.Empty()){
        int from=H.Top().y;H.Pop();
        if(Visit[from])continue;
        Visit[from]=true;
        for(int k=G.Last[from];k!=-1;k=G.Next[k]){
            if(Flow[k]&&Dist[G.To[k]]>Dist[from]+CalcCost(k))
            {
                Dist[G.To[k]]=Dist[from]+CalcCost(k);
                Pre[G.To[k]]=k;H.Push(Pairs<long long,int>(Dist[G.To[k]],G.To[k]));
            }
        }
    }
}
int n;
inline void Augment(const int&id)
{
    int from=G.To[id^1];
    int to=G.To[id];
    if(Flow[id]){
        ++Flow[id];
        return;
    }
    Dijkstra(to);
    if(Dist[from]<Inf&&Dist[from]+CalcCost(id)<0){
        ++Flow[id^1];
        while(from!=to){
            int x=Pre[from];
            --Flow[x];
            ++Flow[x^1];
            from=G.To[x^1];
        }
    }
    else ++Flow[id];
	long long maxi=0;
	long long len=CalcCost(id);
	for(register int i=1;i<=sizev;i++)
		if(Dist[i]<Inf)maxi=Max(maxi,Dist[i]);
	for(register int i=1;i<=sizev;i++)
		Weight[i]+=Dist[i]<Inf?Dist[i]:maxi+Max(0ll,-len);
	Dijkstra(n+1);
	for(register int i=1;i<=sizev;i++)
		Weight[i]+=Dist[i];
}
int m,s,t;
int main(){
	scanf("%d%d%d%d",&n,&m,&s,&t);
	sizev=n;
	for(register int i=1;i<=m;i++){
		int x,y;long long f,w;
		scanf("%d%d%lld%lld",&x,&y,&f,&w);
		if(x==y){if(w<0)mini+=f*w;}
		else Insert(G,x,y,f,0,w,-w);
	}
	Insert(G,t,s,Limit,0,-Limit,Limit);
	for(register int i=1;i<=n;i++){
		Insert(G,n+1,i,0,0,0,0);
		Flow[G.cntm-2]=1;
	}
	for(register int i=40;i>=0;i--){
		for(register int j=0;j<=m*2+1;j++)Flow[j]<<=1;
		for(register int j=0;j<=m*2;j+=2)
			if((G.Flow[j]>>i)&1)
				Augment(j);
	}
	long long mini=0;
	for(register int i=0;i<m;i++)
		mini+=Flow[i<<1|1]*G.Cost[i<<1];
	printf("%lld %lld",Flow[m<<1|1],mini);
	return 0;
}
  1. B e l l m a n F o r d BellmanFord BellmanFord 算法版
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

template<typename Typename>
inline Typename Max(const Typename&x,const Typename&y){
	if(x>y)return x;return y;
}
template<typename Typename>
inline Typename Min(const Typename&x,const Typename&y){
	if(x<y)return x;return y;
}
template<typename Typename>
inline void Swap(Typename&x,Typename&y){
	Typename t=x;x=y;y=t;
}

template<typename Typename,int MaxCnt>
struct Queues{
	private:
		Typename Data[MaxCnt];
		int head,tail,size;
	public:
		Queues(){
			memset(Data,0,sizeof(Data));
			head=tail=size=0;
		}
		inline void Clear(){
			head=tail=size=0;
		}
		inline void Push(const Typename&x){
			if(size==MaxCnt)puts("Error Queue");
			else{
				size++;
				Data[tail]=x;
				tail++;
				if(tail==MaxCnt)tail=0;
			}
		}
		inline void Pop(){
			if(size==0)puts("Error Queue");
			else{
				size--;
				head++;
				if(head==MaxCnt)head=0;
			}
		}
		inline Typename Front(){
			if(size==0){puts("Error Queue");return Typename();}
			else return Data[head];
		}
		inline bool Empty(){return(size==0);}
		inline int Size(){return size;}
};

template<typename Typename,int MaxN,int MaxM>
struct Graphs{
	int Last[MaxN],cntn;
	Typename Cost[MaxM],Flow[MaxM];
	int To[MaxM],Next[MaxM],cntm;
	Graphs(){
		cntn=cntm=0;
		memset(Last,-1,sizeof(Last));
	}
	inline void Clear(){
		cntn=cntm=0;
		memset(Last,-1,sizeof(Last));
	}
};

template<typename Typename>
inline void Insert(Typename&G,int from,int to,long long flow1,long long flow2,long long cost1,long long cost2){
	G.To[G.cntm]=to;G.Flow[G.cntm]=flow1;G.Cost[G.cntm]=cost1;
	G.Next[G.cntm]=G.Last[from];G.Last[from]=G.cntm++;
	
	G.To[G.cntm]=from;G.Flow[G.cntm]=flow2;G.Cost[G.cntm]=cost2;
	G.Next[G.cntm]=G.Last[to];G.Last[to]=G.cntm++;
}

const long long Inf=0x3f3f3f3f3f3f3f3f;
const long long Limit=1ll<<32;
const int MaxN=6e3,MaxM=1e5+1e4;

Graphs<long long,MaxN,MaxM>G;
Queues<int,MaxN>Q;
long long Dist[MaxN],Flow[MaxM];
int Pre[MaxN];
int sizev;

bool Visit[MaxN];

void BellmanFord(int start)
{
	for(register int i=1;i<=sizev+1;i++)
		Visit[i]=false,Dist[i]=Inf,Pre[i]=-1;
    Dist[start]=0;Q.Push(start);
    while(!Q.Empty()){
        int from=Q.Front();Q.Pop();
        for(int k=G.Last[from];k!=-1;k=G.Next[k]){
            if(Flow[k]&&Dist[G.To[k]]>Dist[from]+G.Cost[k])
            {
                Dist[G.To[k]]=Dist[from]+G.Cost[k];
				Pre[G.To[k]]=k;
                if(!Visit[G.To[k]])Q.Push(G.To[k]);
            }
        }
        Visit[from]=false;
    }
}
int n;
inline void Augment(const int&id)
{
    int from=G.To[id^1];
    int to=G.To[id];
    if(Flow[id]){
        ++Flow[id];
        return;
    }
    BellmanFord(to);
    if(Dist[from]<Inf&&Dist[from]+G.Cost[id]<0){
        ++Flow[id^1];
        while(from!=to){
            int x=Pre[from];
            --Flow[x];
            ++Flow[x^1];
            from=G.To[x^1];
        }
    }
    else ++Flow[id];
}
int m,s,t;
int main(){
	scanf("%d%d%d%d",&n,&m,&s,&t);
	sizev=n;
	long long mini=0;
	for(register int i=1;i<=m;i++){
		int x,y;long long f,w;
		scanf("%d%d%lld%lld",&x,&y,&f,&w);
		if(x==y){if(w<0)mini+=f*w;}
		else Insert(G,x,y,f,0,w,-w);
	}
	Insert(G,t,s,Limit,0,-Limit,Limit);
	for(register int i=32;i>=0;i--){
		for(register int j=0;j<=m*2+1;j++)Flow[j]<<=1;
		for(register int j=0;j<=m*2;j+=2)
			if((G.Flow[j]>>i)&1)
				Augment(j);
	}
	for(register int i=0;i<m;i++)
		mini+=Flow[i<<1|1]*G.Cost[i<<1];
	printf("%lld %lld",Flow[m<<1|1],mini);
	return 0;
}

Z k w Zkw Zkw算法

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
template<typename Typename,int MaxCnt>
struct Queues{
	private:
		Typename Data[MaxCnt];
		int head,tail,size;
	public:
		Queues(){
			memset(Data,0,sizeof(Data));
			head=tail=size=0;
		}
		inline void Clear(){
			head=tail=size=0;
		}
		inline void Push(const Typename&x){
			if(size==MaxCnt)puts("Error Queue");
			else{
				size++;
				Data[tail]=x;
				tail++;
				if(tail==MaxCnt)tail=0;
			}
		}
		inline void Pop(){
			if(size==0)puts("Error Queue");
			else{
				size--;
				head++;
				if(head==MaxCnt)head=0;
			}
		}
		inline Typename Front(){
			if(size==0){puts("Error Queue");return Typename();}
			else return Data[head];
		}
		inline bool Empty(){return(size==0);}
		inline int Size(){return size;}
};

template<typename Typename,int MaxN,int MaxM>
struct Graphs{
	int Last[MaxN],cntn;
	Typename Cost[MaxM],Flow[MaxM];
	int To[MaxM],Next[MaxM],cntm;
	Graphs(){
		cntn=cntm=0;
		memset(Last,-1,sizeof(Last));
	}
	inline void Clear(){
		cntn=cntm=0;
		memset(Last,-1,sizeof(Last));
	}
};

template<typename Typename>
inline void Insert(Typename&G,int from,int to,long long flow1,long long flow2,long long cost1,long long cost2){
	G.To[G.cntm]=to;G.Flow[G.cntm]=flow1;G.Cost[G.cntm]=cost1;
	G.Next[G.cntm]=G.Last[from];G.Last[from]=G.cntm++;
	
	G.To[G.cntm]=from;G.Flow[G.cntm]=flow2;G.Cost[G.cntm]=cost2;
	G.Next[G.cntm]=G.Last[to];G.Last[to]=G.cntm++;
}
template<typename Typename>
inline Typename Min(const Typename&x,const Typename&y){
	if(x<y)return x;return y;
}
template<typename Typename>
inline Typename Max(const Typename&x,const Typename&y){
	if(x<y)return x;return y;
}

const long long Inf=0x3f3f3f3f3f3f3f3f;
const int MaxN=1100;
const int MaxM=201000;

Graphs<long long,MaxN,MaxM>G;
Queues<int,MaxN>Q;
int st,ed,sizev;
bool Visit[MaxN];
long long Dist[MaxN];
inline bool BellmanFord(){
	for(register int i=1;i<=sizev;i++)
		Visit[i]=false,Dist[i]=Inf;
	Q.Clear();Q.Push(ed);
	Visit[ed]=true;Dist[ed]=0;
	while(!Q.Empty()){
		int from=Q.Front();Q.Pop();
		for(register int k=G.Last[from];k!=-1;k=G.Next[k]){
			if(G.Flow[k^1]>0&&G.Cost[k^1]+Dist[from]<Dist[G.To[k]]){
				Dist[G.To[k]]=G.Cost[k^1]+Dist[from];
				if(Visit[G.To[k]]==false){
					Visit[G.To[k]]=true;
					Q.Push(G.To[k]);
				}
			}
		}
		Visit[from]=false;
	}
	return Dist[st]!=Inf;
}
int Cur[MaxN];
long long cost;
inline long long Dfs(int from,long long flow){
	if(from==ed)return flow;
	Visit[from]=true;
	long long used=0,test;
	for(register int k=Cur[from];k!=-1;k=G.Next[k]){
		if(!Visit[G.To[k]]&&G.Flow[k]>0&&Dist[from]-G.Cost[k]==Dist[G.To[k]]){
			used+=(test=Dfs(G.To[k],Min(G.Flow[k],flow-used)));
			G.Flow[k]-=test;G.Flow[k^1]+=test;cost+=test*G.Cost[k];
			Cur[from]=k;
			if(used==flow){Visit[from]=false;return used;}
		}
	}
	Visit[from]=false;
	return used;
}
int n,m;
int main(){
	scanf("%d%d%",&n,&m);
	st=1;ed=n;sizev=n;
	for(register int i=1;i<=m;i++){
		int x,y;long long f,c;
		scanf("%d%d%lld%lld",&x,&y,&f,&c);
		if(x==y){if(c<0)cost+=f*c;}//自环的处理 
		else Insert(G,x,y,f,0,c,-c);
	}
	long long ans=0;
	while(BellmanFord()){
		memcpy(Cur+1,G.Last+1,sizeof(int)*n);
		while(test=Dfs(st,Inf))ans+=test;
	}
	printf("%lld %lld",ans,cost);
	return 0;
}

上下界网络流

概述

上下界网络流本质是给流量网络的每一条边设置了流量上界 u ( u , v ) u(u,v) u(u,v) 和流量下界 l ( u , v ) l(u,v) l(u,v) . . . 也就是说 , , , 一种可行的流必须满足 l ( u , v ) ≤ f ( u , v ) ≤ u ( u , v ) l(u,v)\le f(u,v) \le u(u,v) l(u,v)≤f(u,v)≤u(u,v) . . . 同时必须满足除了源点和汇点之外的其余点流量平衡 . . .

根据题目要求 , , , 我们可以使用上下界网络流解决不同问题 . . .

无源汇上下界可行流

给定无源汇流量网络 G G G . . . 询问是否存在一种标定每条边流量的方式 , , , 使得每条边流量满足上下界同时每一个点流量平衡 . . .

不妨假设每条边已经流了 l ( u , v ) l(u,v) l(u,v) 的流量 , , , 设其为初始流 . . . 同时我们在新图中加入 u u u 连向 v v v 的流量为 u ( u , v ) − l ( u , v ) u(u,v)-l(u,v) u(u,v)−l(u,v) 的边 . . . 考虑在新图上进行调整 . . .

由于最大流需要满足初始流量平衡条件 ( ( ( 最大流可以看成是下界为 的上下界最大流 ) ) ) , , , 但是构造出来的初始流很有可能不满足初始流量平衡 . . . 假设一个点初始流入流量减初始流出流量为 M M M . . .

若 M = 0 M=0 M=0 , , , 此时流量平衡 , , , 不需要附加边 . . .

若 M > 0 M>0 M>0 , , , 此时入流量过大 , , , 需要新建附加源点 S ′ S' S′ , , , S ′ S' S′ 向其连流量为 M M M 的附加边 . . .

若 M < 0 M<0 M<0 , , , 此时出流量过大 , , , 需要新建附加汇点 T ′ T' T′ , , , 其向 T ′ T' T′ 连流量为 − M -M −M 的附加边 . . .

如果附加边满流 , , , 说明这一个点的流量平衡条件可以满足 , , , 否则这个点的流量平衡条件不满足 . . . ( ( ( 因为原图加上附加流之后才会满足原图中的流量平衡 ) ) )

在建图完毕之后跑 S ′ S' S′ 到 T ′ T' T′ 的最大流 , , , 若 S ′ S' S′ 连出去的边全部满流 , , , 则存在可行流 , , , 否则不存在 . . .

有源汇上下界可行流

给定有源汇流量网络 G G G . . . 询问是否存在一种标定每条边流量的方式 , , , 使得每条边流量满足上下界同时除了源点和汇点每一个点流量平衡 . . .

假设源点为 S S S , , , 汇点为 T T T . . .

则我们可以加入一条 T T T 到 S S S 的上界为 ∞ \infty ∞ , , , 下界为 0 0 0 的边转化为无源汇上下界可行流问题 . . .

若有解,则 S S S 到 T T T 的可行流流量等于 T T T 到 S S S 的附加边的流量 . . .

有源汇上下界最大流

给定有源汇流量网络 G G G . . . 询问是否存在一种标定每条边流量的方式 , , , 使得每条边流量满足上下界同时除了源点和汇点每一个点流量平衡 . . . 如果存在,询问满足标定的最大流量 . . .

我们找到网络上的任意一个可行流 . . . 如果找不到解就可以直接结束 . . .

否则我们考虑删去所有附加边之后的残量网络并且在网络上进行调整 . . .

我们在残量网络上再跑一次 S S S 到 T T T 的最大流 , , , 将可行流流量和最大流流量相加即为答案 . . .

S S S 到 T T T 的最大流直接在跑完有源汇上下界可行的残量网络上跑 . . .

千万不可以在原来的流量网络上跑 . . .

有源汇上下界最小流

给定有源汇流量网络 G G G . . . 询问是否存在一种标定每条边流量的方式 , , , 使得每条边流量满足上下界同时除了源点和汇点每一个点流量平衡 . . . 如果存在 , , ,询问满足标定的最小流量 . . .

类似的 , , , 我们考虑将残量网络中不需要的流退掉 . . .

我们找到网络上的任意一个可行流 , , , 如果找不到解就可以直接结束 . . .

否则我们考虑删去所有附加边之后的残量网络 . . .

我们在残量网络上再跑一次 T T T 到 S S S 的最大流 , , , 将可行流流量减去最大流流量即为答案 . . .

F r o m From From OI-wiki

构图技巧

点边互化

有向图中 , , , 若点 x x x 作为起点 , , , 则连有向边 ( x ′ , y ) (x',y) (x′,y) , , , 否则连有向边 ( y , x ) (y,x) (y,x) . . .
最后使所有点 x x x , , , 连边 ( x , x ′ ) (x,x') (x,x′) . . .

无向图中 , , , 若有边 ( x , y ) (x,y) (x,y) , , , 则连有向边 ( x ′ , y ) (x',y) (x′,y) 和有向边边 ( y ′ , x ) (y',x) (y′,x) . . .
最后使所有点 x x x , , , 连边 ( x , x ′ ) (x,x') (x,x′) . . .

任何图中 , , , 边 ( x , y ) (x,y) (x,y) 可以变为 ( x , e ) (x,e) (x,e) 和 ( e , y ) (e,y) (e,y) , , , e e e 是新建的节点 , , , 注意新边的方向 . . .

最好自己用手模拟一遍 . . .

libfacedetection 框架阅读及扩展经验 libfacedetection 框架源码解析与扩展心得1. libfacedetection 框架简介2. 源码解析2.1 facedetectcnn.h2.2 facedetectcnn.cpp2.3 facedetectcnn-model.cpp3. 其他层的扩展--depthwise convolution 前段时间因为工作需要,对libfacedetection框架做了些扩展,在阅读源... 阅读详情

相关推荐

高通Camera IFE时钟配置

高通Camera IFE时钟配置中的minHorizontalBlanking和minVerticalBlanking配置计算方法

一个懒癌的驱动工程师的随笔分享 6167

专题十一 网络流

文章目录专题十一 网络流POJ 3436 ACM Computer FactoryPOJ 3281 DiningPOJ 1087 A Plug for UNIXPOJ 2195 Going HomePOJ 2516 Minimum CostPOJ 1459 Power NetworkHDU 4280 Island TransportHDU 4292 FoodHDU 4289 ControlUVA 10480 SabotageHDU 2732 Leapin' LizardsHDU 3338 Kakuro Ex

m0_49959202的博客 189

科研软件|Adobe Photoshop 2024 (PS 2024)

科研软件|Adobe Photoshop 2024 (PS 2024)

m0_55499682的博客 5508

【网络流专题】

<br />最近做了两道网络流神题,里面用了几个非常巧妙的技巧,在今后很多题目中都可以使用得到。<br />第一题:WC2007石头剪刀布<br />      这题是网络流,简直太难想到了,总之这道题有几个非常巧妙的转化。<br />      首先是补集转化,我要统计一个完全有向图中的三元环的个数,就等于所有环的个数-非三元环的个数。非三元环的个数怎么统计呢?观察三元环的特点,发现一个三元环必然有一个点在这个环中入度为二(出度为二),由此我们便可以得到一个计算三元环的公式(设d[i]为i的入度)<br

Evil.livE 1220

雷达气象学(8)——反射率因子图实战(强对流预警篇)

本文聚焦雷达反射率因子图在强对流预警中的实战应用,深入解析了柱状回波、有界弱回波区(BWER)、三体散射(TBSS)等关键回波结构特征,这些是识别超级单体风暴和大冰雹的核心“身份证”。文章强调从静态识别转向动态追踪与多源数据综合研判的思维转变,并提供了系统的实战分析流程与常见误区避坑指南,旨在提升对强对流天气的短临预警能力。

weixin_29213827的博客 485

避开PFC设计大坑:从环路分析到参数整定的5个常见误区与解决方案

本文深入解析PFC电路设计中从环路分析到参数整定的5个常见误区,包括电感参数设计、小信号模型适用边界、电压环与电流环耦合效应等。通过实际案例和PSIM仿真数据,提供工程落地的解决方案,帮助工程师避开设计陷阱,提升电路性能与可靠性。

weixin_30666401的博客 325

专题·网络流【资源集合

首先是入门:https://oi-wiki.org/graph/flow/ 各个专题:最大流,最小割,费用流【最大和最小差不多,上下界网络流 建模技巧总汇:https://www.cnblogs.com/victorique/p/8560656.html ...

樱狸✨愿携清风归来处 2034

Qt绘图库对决:QWT与QCustomPlot的实战选型指南

本文为Qt开发者提供了QWT与QCustomPlot两大绘图库的实战选型指南。文章从功能特性、性能资源、开发效率三大维度进行深度对比,并结合工业监控、科学计算、嵌入式设备等典型场景,给出了清晰的决策清单。对于需要专业科学图表(如极坐标、等高线图)的场景推荐QWT;对于追求高性能、实时性、快速开发及嵌入式部署的通用图表需求,QCustomPlot是更优选择。

alpha的博客 241

【专题总结】 网络流

day1 (最大流) 一、问题导向: 1)有网络模型 2)问题的可行性 3)n^2迷之复杂度 二、模板理解 1)EK算法 一个基础:增广路。 只要能实现更正之前的操作,枚举完所有情况得到的一定是最优解(之一) 更正实现:建立反向弧(增减与原边相反的反向边) e[1]== u-&amp;gt;v c(capacity) e[2]== v-&amp;gt;u 0 为何反向弧能实现更改? 可以和匈牙利算法相...

Jun 421

使用 EasyExcel 实现 百万级数据导入导出

在项目开发中往往需要使用到数据的导入和导出,导入就是从Excel中导入到DB中,而导出就是从DB中查询数据然后使用POI写到Excel上。大数据的导入和导出,相信大家在日常的开发、面试中都会遇到。很多问题只要这一次解决了,总给复盘记录,后期遇到同样的问题就好解决了。好啦,废话不多说开始正文!1.如此大批量数据的导出和导入操作,会占用大量的内存实际开发中还应限制操作人数。2.在做大批量的数据导入时,可以使用jdbc手动开启事务,批量提交。

houxian1103的博客 2699

京东运维面试题及参考答案

cgroup(Control Group)是 Linux 内核提供的一种可以限制、控制与分离一个进程组群的资源(如 CPU、内存、磁盘 I/O 等)的机制。通过 cgroup,可以为不同的进程或进程组分配特定的资源配额,实现资源的隔离和管理。比如,在一个服务器上运行多个容器时,可以使用 cgroup 来限制每个容器对 CPU 和内存的使用,防止某个容器占用过多资源而影响其他容器的运行。cgroup 还可以对进程进行优先级设置,确保关键进程能够优先获得资源。

大模型大数据攻城狮的专栏 1236

【网络流】专题总结

发现好久没来写总结了 做了一星期+的网络流 还讲了两节课 是时候写个总结了 分类: 最大流:用sap做 如果是平面图 可以对偶转换再求最短路 最小割:求法和最大流一样 方案可以用bfs求 费用流:用spfa做 如果时间过不了可以用zkw费用流(虽然我还不会- -) 上下界: 建立虚拟源、汇 求虚拟源汇最大流 若满流则有解 最大流...

dee2784的博客 173

6、Dialogflow 开发全解析:从基础到高级应用

本文全面解析了 Dialogflow 的核心功能与高级应用,涵盖意图与实体的创建、上下文管理、后续意图设置以及高级实体类型的实际应用场景。详细介绍了自定义实体的导入导出规则、编程添加方法及数量限制,并通过流程图和实例说明了上下文生命周期与对话连贯性设计。结合电商、旅游、问卷调查等实际业务场景,展示了模糊匹配、自动扩展、复合实体等功能的强大灵活性。最后提供了开发最佳实践建议,帮助开发者构建高效、智能的对话系统。

kafka6courier的博客 103

NOIp 图论算法专题总结 (3):网络流 & 二分图 简明讲义

系列索引: NOIp 图论算法专题总结 (1) NOIp 图论算法专题总结 (2) NOIp 图论算法专题总结 (3) 网络流 概念 1 容量网络(capacity network)是一个有向图,图的边 \((u, v)\) 有非负的权 \(c(u, v)\),被称为容量(capacity)。 图中有一个被称为源(source)的节点和一个被称为汇(sink)的节点。图中每条边称为弧(ar...

weixin_33836874的博客 390

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

[kuangbin带你飞]专题十一 网络流

专题十一 网络流 模板: SAP + GAP 邻接表:https://blog.youkuaiyun.com/Adolphrocs/article/details/84779575 SAP + GAP 邻接矩阵:https://blog.youkuaiyun.com/Adolphrocs/article/details/84779661 dinic 邻接矩阵:https://blog.youkuaiyun.com/Adolp...

Adolphrocs的博客 485

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

专题 网络流从放弃到坚持放弃

文章目录基本概念与常用定理1. 基本概念1.1 网络流1.2 割1.3 费用流2. 常用定理Dinic算法1.求解最大流2.求解最小费用最大流 基本概念与常用定理 1. 基本概念 1.1 网络流 网络流图:带权的有向图G=(V,E)G=(V,E)G=(V,E),满足以下条件,则称为网络流图(flownetwork)(flow network)(flownetwork): 仅有一个入度为000的顶...

weixin_44577381的博客 215

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

专题:网络流问题

挖坑

zehong1995的博客 408

“kuangbin带你飞”专题计划——专题十一:网络流

写在前面 开始时间2021-04-27,结束时间未知。 怕又是一周的高效学习,然后又是乏了颓废一周。。。那就一周搞完这一专题,发出咯咯的笑声。 1.从未接触过,听说很难emmm。

I_have_a_world的博客 384

kuangbin专题十一网络流总结

这个专题是之前做的专题中做的最轻松的,毕竟只要想出建图就ok了。 建图有两个套路。如果是对一条边限制的话,那么就可以直接把容量变成那个限制就行了。而对点有限制的话,那么我们可以进行拆点,把点的限制转化成边限制。先来一道经典的建图题,只要把这题体会清楚了,那么之后的建图也就会清晰很多。 A - ACM Computer Factory 把工厂拆成2个点,cap就为他最大的容量。之后根据他们的关...

qq_34921856的博客 525

网络流专题练习

网络流之二分图匹配、关键边、最大流判定、拆点

HeartWine的博客 350
上一篇: 杂七杂八的特性
下一篇: Cpp泛型
DarksideCoder
博客等级 码龄7年 34粉丝 29原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值