网络流专题

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

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

MicroPython ubluetooth 库详细介绍 MicroPython的ubluetooth库为蓝牙通信提供了强大的支持,使得开发者能够在MicroPython环境下轻松实现蓝牙设备之间的通信。其中ubluetooth库为MicroPython提供了强大的蓝牙通信功能,使得开发者能够轻松地在各种支持MicroPython的硬件平台上实现蓝牙设备之间的通信。它提供了一系列的函数和类,用于实现蓝牙设备的扫描、连接、数据传输等功能。- 上述代码中,首先创建了一个蓝牙服务和一个特征,然后可以通过read和write函数读取和写入特征值。# 处理连接成功事件。 阅读详情

相关推荐

从零开始:用摩尔线程S4000运行开源AI项目的完整流程

本文详细介绍了如何使用摩尔线程S4000 GPU从零开始运行开源AI项目的完整流程,包括硬件选择、驱动安装、容器化环境配置以及典型AI项目的实战部署。通过性能优化技巧,显著提升推理速度,适合开发者快速上手国产GPU的AI应用开发。

weixin_29231725的博客 581

专题十一 网络流

文章目录专题十一 网络流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

别再死记硬背了!用Amesim HCD库亲手搭建单向阀、锥阀、球阀模型(附详细参数设置)

本文详细介绍了如何使用Amesim HCD库从零构建单向阀、锥阀和球阀的仿真模型,帮助工程师摆脱死记硬背,掌握物理建模的核心技巧。通过实战案例和参数设置指南,读者将学会如何优化阀门动态响应、验证流量特性以及处理旋转运动等独特挑战,提升液压系统设计效率。

weixin_29248611的博客 499

【网络流专题】

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

Evil.livE 1220

CTF实战:从ECDSA签名漏洞到flag获取

本文详细解析了ECDSA签名算法在CTF比赛中的实战应用,重点探讨了随机数重用和参数泄露等常见漏洞。通过PWNHUB六月赛的例题,展示了如何从签名数据中恢复私钥并获取flag,同时提供了防御措施和实战技巧,帮助参赛者提升解题能力。

weixin_42706667的博客 276

企业老板管理者,如何监控员工的电脑屏幕?

老板监控员工电脑,需要知道员工开机后的上网行为来及时了解到员工工作情况,及时发现上班不良行为员工,及时淘汰低价值员工。 文章目录iMonitor EAM一、计算机系统日志二、具体功能介绍1.屏幕记录2.键盘记录总结 iMonitor EAM 怎么监控员工电脑,我们需要用到第三方软件,2021年最新版本软件-iMonitor EAM,软件分管理端和客户端,管理端在管理者电脑登录,客户端在员工电脑双击安装就可以,安装中不用任何设置的,客户端安装到员工电脑后也是隐藏的,不会有任何弹窗和警告信息,是无法发现和删

iMonitorSoft的博客 2179

专题·网络流【资源集合

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

樱狸✨愿携清风归来处 2034

PCB板设置20H 原则理解

20H原则是PCB设计中抑制EMI辐射的关键方法,要求电源层比相邻地层内缩20倍介质厚度(20H)。在四层板中实现20H原则需采用S-G-P-S层叠结构,确保GND与PWR紧邻。具体步骤包括:确定GND-PWR间介质厚度H,计算20H值(通常80-120mil),并在设计软件中将电源层四周内缩20H,同时保持地层完整不内缩。注意避免常见误区,如混淆介质厚度、地层内缩等。该原则能有效限制边缘场辐射,降低EMI干扰,是四层板EMC设计的重要规范。

qq_37999850的博客 690

【专题总结】 网络流

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

基于matlab的CNN食物识别分类系统,matlab深度学习分类,训练+数据集+界面

饮食在人们的日常生活、营养与医疗建议以及运动员等专业人士的训练上起着越来越重要的作用。随着互联网的发展和医学的进步,人们普遍上传分享和记录的食物图像形成了非常多的数据库,为了改善饮食结构,塑造更健康的生活方式,分析食物的种类、热量和进食时间成为了营养学上非常重要的研究方法,本文的使用深度学习算法可以根据用户上传的食物图像自动分析食物种类。深度学习是机器学习领域的一个研究方向。深度学习通过对数据特征的学习将原始数据转化为计算机可以理解的抽象数据,根据学习到的特征,可以对原始数据进行检测或分类。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 4720

SAPIEN ManiSkill3 入门教程(一)

请注意,table.set_pose(pose) 设置的是演员在世界帧中的坐标系姿势。移除的实体可以再次添加到场景中,但已在场景中的实体则不能再次添加。显示器可以是物理的(如显示器),也可以是虚拟的(如 VNC)。我们可以通过 get_pose()、get_linear_velocity() 和 get_angular_velocity()获取演员的运动量(姿势、线速度、角速度)。现在,让我们创建一个斜坡。例如,您可能需要一个简单的碰撞形状来进行快速模拟,但却需要一个复杂的视觉形状来进行逼真的渲染。

机器人控制与仿真应用 3072

【网络流】专题总结

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

dee2784的博客 173

rk3576 点亮 LCD(eDP)

RK3576 处理器集成了对 **eDP (Embedded DisplayPort)** 接口的支持,主要用于驱动嵌入式或笔记本电脑的内置屏幕。支持 **eDP 1.3 版本协议**, 最大可支持 ****4K@120Hz**。

weixin_35723192的博客 352

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

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

weixin_33836874的博客 390

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

[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

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

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

文章目录基本概念与常用定理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

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

专题:网络流问题

挖坑

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、付费专栏及课程。

余额充值