网络流专题

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

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

ArcGIS栅格数据像元值与坐标值提取及Excel导出全流程解析 本文详细解析了使用ArcGIS将栅格数据批量提取像元值与坐标值并导出至Excel的全流程。通过“栅格转点”、“添加XY坐标”和“表转Excel”三大核心步骤,无需编程即可自动化处理遥感、环境等领域的空间数据,有效提升数据分析效率。 阅读详情

相关推荐

CUDA系列-Stream-1

This section describes static aspects of the CUSW_UNIT_STREAM unit’s architecture.和stream相关的其它Unit主要有Channel,Sync,Memmgr A CUDA stream is a software FIFO queue that allows commands like kernel launch and memcpy to be enqueued in it. The enqueued commands

weixin_43360707的博客 734

专题十一 网络流

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

ESP32-CAM图传小车:端边协同的嵌入式视觉系统设计

在物联网边缘智能场景中,嵌入式视觉系统需平衡实时性、算力与成本。其核心在于理解图像采集、硬件编码、低延迟传输与分层计算的基本原理——OV2640等CMOS传感器配合JPEG硬件编码可显著降低主控负载;TCP流式协议比HTTP/RTSP更适配资源受限MCU;而‘计算卸载’架构将OpenCV等重负载移至Android端,使ESP32专注实时控制与传输,既发挥FreeRTOS任务调度优势,又规避了PSRAM带宽与内存瓶颈。该模式广泛适用于教育机器人、智能巡检及DIY视觉终端等场景,本文以WiFi图传坦克小车为载体

weixin_42524864的博客 373

【网络流专题】

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

Evil.livE 1220

DLMS—Cryptographic algorithms—(Green-Book)加密算法

密码学是数学的一个分支,基于数据的转换,并且可以用来提供多种安全服务:保密性、数据完整性、认证、授权和不可否认性。密码学依赖于两个基本组成部分:算法(或密码学方法)和密钥。算法是一个数学函数,而密钥是在转换过程中使用的参数。密码算法和密钥用于对数据应用密码保护(例如,加密数据或生成数字签名),以及移除或检查保护(例如,解密加密的数据或验证数字签名)。密码哈希函数:这类函数不需要密钥(尽管它们可以以使用密钥的模式使用)。哈希函数通常用作算法的一个组成部分,以提供安全服务。见9.2.3.2节。对称密钥算法。

一个bug是bug,多个bug能work 1707

自动化学报latex模板——ctex2.4.6和ctex2.9.2直接使用步骤

自动化学报latex模板——ctex2.4.6和ctex2.9.2直接使用步骤 1.背景及问题 最近在使用自动化学报latex模板写作业时,一只对latex搞不清楚,网上关于各种版本的latex下载了一堆(比如ctex中文套装方案的各个版本及texlive+texstudio方案,中间还升级了miktex,windet等),发现都未能成功编译出来自动化学报的latex模板,反正开始头很大!!!最后终于没忍住打开了: 自动化学报官网下载模板链接,因为我就要用latex模板(不能服输!)找到的解决方案是:使用

QIANFANGshiwo的博客 7709

专题·网络流【资源集合

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

樱狸✨愿携清风归来处 2034

190、LLC谐振变换器的样机调试实战(问题定位)

这篇实战笔记总结了LLC谐振变换器样机调试中的五大典型问题及解决方案:1)空载炸机源于谐振电容纹波电流能力不足;2)轻载啸叫因控制环路不稳定,需调整突发模式阈值;3)MOSFET过热由死区时间不当导致,需精确优化实现ZVS;4)输出纹波大由PCB布局耦合引起,需优化共模抑制;5)启动过冲需延长软启动时间。作者强调调试需结合示波器、热成像仪等工具,理论计算与实测并重,并详细记录每次参数调整。文章指出LLC调试是不断试错的过程,关键是通过现象分析本质原因。

Jack010169的博客 48

【专题总结】 网络流

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

GitHub 国内加速完全指南(最新):镜像、代理与优化技巧

在国内使用 GitHub 时,经常遇到 clone 慢、打开仓库卡、GitHub Actions 不稳定 等问题。下面是整理好的 GitHub 国内镜像和加速方法大全,高效使用 GitHub。

moton2017的博客 1万+

6、基因组数据可视化与甲基化分析:从基础操作到深入洞察

本文详细介绍了基因组数据可视化和全基因组DNA甲基化分析的方法和技术,包括基因组查看器(IGV)的基本操作、单核苷酸变异的可视化、DNA甲基化与CpG岛的关联分析、统计建模与显著甲基化区域识别,以及不同细胞系之间的甲基化模式差异分析。通过具体案例展示了如何利用MATLAB进行数据分析和可视化,为基因组研究、疾病机制探索及个性化医疗提供了技术支撑和实践参考。

qsc901234的博客 295

【网络流】专题总结

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

dee2784的博客 173

别让 Claude Code 一直问你“能不能执行“:权限配置完全指南

Claude Code 提供了一套精细的权限配置机制,配置好权限设置,可以提速不少。这个小技巧需要掌握一下。

沐风 3840

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

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

weixin_33836874的博客 390

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

VC中sqlite的使用

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

[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

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

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

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

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

专题:网络流问题

挖坑

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

余额充值