【学习笔记】网络流

# 〇:【前言】

2026.1.29竣工

# 壹:【板子】

网络流有五个常见优化:分层图,多路增广,当前弧优化,空流优化,炸点优化,

分层图和多路增广组成了Dinic

当前弧优化是保证Dinic时间复杂度正确的最重要的优化

而空流优化和炸点优化,优化得并不是很明显

P3376 【模板】网络最大流
#include<bits/stdc++.h>
#define inf 1e18
using namespace std;int n,m,s,t;
typedef long long LL;
const int N=210,M=1e4+10;
int h[N],to[M],w[M],ne[M],idx=1;
void add(int a,int b,int c){
	to[++idx]=b;ne[idx]=h[a];h[a]=idx;w[idx]=c;
}
int dep[N],now[N];
bool bfs(){//分层图
	for(int i=1;i<=n;i++){
		dep[i]=-1;
		now[i]=h[i];
	}
	queue<int> q;q.push(s);dep[s]=1;
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dep[v]==-1){
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	}
	return dep[t]!=-1;
}
LL dfs(int u,LL las){//多路增广
	if(u==t) return las;
	LL ans=0;
	for(int i=now[u];i&&las;i=ne[i]){//空流优化
		int v=to[i];
		now[u]=i;//当前弧优化
		if(w[i]&&dep[u]+1==dep[v]){
			int res=dfs(v,min(las,(LL)w[i]));
			ans+=res;
			las-=res;
			w[i]-=res;
			w[i^1]+=res;
		}
	}
	if(ans==0) dep[u]=-1;//炸点优化
	return ans;
}
LL Dinic(){
	LL ans=0;
	while(bfs()) ans+=dfs(s,inf);
	return ans;
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n>>m>>s>>t;
	while(m--){
		int a,b,c;cin>>a>>b>>c;
		add(a,b,c);
		add(b,a,0);
	}
	cout<<Dinic()<<"\n";
	return 0;
}
P3381 【模板】最小费用最大流
#include<bits/stdc++.h>
#define inf 1e9+10
using namespace std;
const int N=5010,M=1e5+10;int n,m,s,t;
int h[N],to[M],w[M],c[M],ne[M],idx=1;
int dis[N],vis[N];
int now[N];
void add(int u,int v,int ww,int cc){
	to[++idx]=v;w[idx]=ww;ne[idx]=h[u];h[u]=idx;c[idx]=cc;
}
bool bfs(){
	for(int i=1;i<=n;i++){
		now[i]=h[i];
		dis[i]=inf;
	}
	queue<int> q;q.push(s);dis[s]=0;vis[s]=1;
	while(q.size()){
		int u=q.front();q.pop();
		vis[u]=0;
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dis[v]>dis[u]+c[i]){
				dis[v]=dis[u]+c[i];
				if(vis[v]) continue;
				vis[v]=1;
				q.push(v);
			}
		}
	}
	return dis[t]!=inf;
}
int cosn=0;
int dfs(int u,int las){
	if(u==t) return las;
	int ans=0;
	vis[u]=1;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(dis[v]==dis[u]+c[i]&&w[i]&&!vis[v]){
			int res=dfs(v,min(las,w[i]));
			ans+=res;
			las-=res;
			w[i]-=res;
			w[i^1]+=res;
			cosn+=res*c[i];
		}
	}
	vis[u]=0;
	if(ans==0) dis[u]=inf;
	return ans;
}
int Dinic(){
	int ans=0;
	while(bfs()) ans+=dfs(s,inf);
	return ans;
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n>>m>>s>>t;
	while(m--){
		int u,v,w,c;cin>>u>>v>>w>>c;
		add(u,v,w,c);
		add(v,u,0,-c);
	}
	cout<<Dinic()<<" ";
	cout<<cosn<<"\n";
	return 0;
}
P14578 【模板】无源汇上下界可行流
#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=1010,M=(1e4+2*N)*2;int n,m,s,t;
int h[N],ne[M],w[M],to[M],idx=1;
int beg[M];
int in[N];
void add(int a,int b,int c){
	to[++idx]=b;ne[idx]=h[a];h[a]=idx;w[idx]=c;
}
int Graph(){
	for(int i=1;i<=m;i++){
		int a,b,l,r;cin>>a>>b>>l>>r;
		r-=l;
		add(a,b,r);
		add(b,a,0);
		in[b]+=l;in[a]-=l;
		beg[i]=l;
	}
	s=n+1;t=n+2;
	int ans=0;
	for(int i=1;i<=n;i++){
		if(in[i]>0){
			add(s,i,in[i]);
			add(i,s,0);
			ans+=in[i];		
		}
		else{
			add(i,t,-in[i]);
			add(t,i,0);
		}
	}
	return ans;
}
int dep[N],now[N];
bool bfs(){
	for(int i=0;i<N;i++){
		dep[i]=-1;
		now[i]=h[i];
	}
	queue<int> q;q.push(s);dep[s]=1;
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dep[v]==-1){
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	}
	return dep[t]!=-1;
}
int dfs(int u,int las){
	if(u==t) return las;
	int ans=0;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&dep[v]==dep[u]+1){
			int res=dfs(v,min(las,w[i]));
			las-=res;
			ans+=res;
			w[i]-=res;
			w[i^1]+=res;
		}
	}
	if(ans==0) dep[u]=-1;
	return ans;
}
int Dinic(){
	int ans=0;
	while(bfs()) ans+=dfs(s,inf);
	return ans;
}
int main(){
	cin>>n>>m;
	int num=Graph();
	int ans=Dinic();
	if(num!=ans) cout<<"No\n";
	else{
		cout<<"Yes\n";
		for(int i=1;i<=m;i++) cout<<beg[i]+w[i*2+1]<<"\n";
	}
	return 0;
}
P14579 【模板】有源汇上下界最大流
#include<bits/stdc++.h>
#define inf 1e9+10
using namespace std;
const int N=1010,M=(1e4+N*2)*2;int n,m,s,t,ss,tt;
int in[N];
int h[N],to[M],w[M],ne[M],idx=1;
int add(int a,int b,int c){
	to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
	return idx;
}
int id;
int Graph(){
	for(int i=1;i<=m;i++){
		int a,b,l,r;cin>>a>>b>>l>>r;
		r-=l;
		add(a,b,r);
		add(b,a,0);
		in[b]+=l;in[a]-=l;
	}
	add(t,s,inf);
	id=add(s,t,0);
	
	ss=n+1,tt=n+2;
	int ans=0;
	for(int i=1;i<=n;i++){
		if(in[i]>0){
			ans+=in[i];
			add(ss,i,in[i]);
			add(i,ss,0);
		}
		else{
			add(i,tt,-in[i]);
			add(tt,i,0);
		}
	}
	return ans;
}
int dep[N],now[N];
bool bfs(int s,int t){
	for(int i=0;i<N;i++){
		dep[i]=-1;
		now[i]=h[i];
	}
	queue<int> q;q.push(s);dep[s]=1;
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dep[v]==-1){
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	}
	return dep[t]!=-1;
}
int dfs(int u,int las,int t){
	if(u==t) return las;
	int ans=0;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&dep[v]==dep[u]+1){
			int res=dfs(v,min(las,w[i]),t);
			las-=res;
			ans+=res;
			w[i]-=res;
			w[i^1]+=res;
		}
	}
	if(ans==0) dep[u]=-1;
	return ans;
}
int Dinic(int s,int t){
	int ans=0;
	while(bfs(s,t)) ans+=dfs(s,inf,t);
	return ans;
}
int main(){
	cin>>n>>m>>s>>t;
	int num=Graph();
	int ans=Dinic(ss,tt);
	if(num!=ans) cout<<"N\n";
	else{
		int ans=w[id];
		w[id]=w[id^1]=0;
		cout<<ans+Dinic(s,t)<<"\n";
	}
	return 0;
}
P14580 【模板】有源汇上下界最小流
#include<bits/stdc++.h>
#define inf 1e9+10
using namespace std;
const int N=1010,M=(1e4+N*2)*2;int n,m,s,t,ss,tt;
int in[N];
int h[N],to[M],w[M],ne[M],idx=1;
int add(int a,int b,int c){
	to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
	return idx;
}
int id;
int Graph(){
	for(int i=1;i<=m;i++){
		int a,b,l,r;cin>>a>>b>>l>>r;
		r-=l;
		add(a,b,r);
		add(b,a,0);
		in[b]+=l;in[a]-=l;
	}
	add(t,s,inf);
	id=add(s,t,0);
	
	ss=n+1,tt=n+2;
	int ans=0;
	for(int i=1;i<=n;i++){
		if(in[i]>0){
			ans+=in[i];
			add(ss,i,in[i]);
			add(i,ss,0);
		}
		else{
			add(i,tt,-in[i]);
			add(tt,i,0);
		}
	}
	return ans;
}
int dep[N],now[N];
bool bfs(int s,int t){
	for(int i=0;i<N;i++){
		dep[i]=-1;
		now[i]=h[i];
	}
	queue<int> q;q.push(s);dep[s]=1;
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dep[v]==-1){
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	}
	return dep[t]!=-1;
}
int dfs(int u,int las,int t){
	if(u==t) return las;
	int ans=0;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&dep[v]==dep[u]+1){
			int res=dfs(v,min(las,w[i]),t);
			las-=res;
			ans+=res;
			w[i]-=res;
			w[i^1]+=res;
		}
	}
	if(ans==0) dep[u]=-1;
	return ans;
}
int Dinic(int s,int t){
	int ans=0;
	while(bfs(s,t)) ans+=dfs(s,inf,t);
	return ans;
}
int main(){
	cin>>n>>m>>s>>t;
	int num=Graph();
	int ans=Dinic(ss,tt);
	if(num!=ans) cout<<"N\n";
	else{
		int ans=w[id];//cout<<ans<<"\n";
		w[id]=w[id^1]=0;
		cout<<ans-Dinic(t,s)<<"\n";
	}
	return 0;
}

# 贰:【二十四题选做】

题单
以下内容经过总结,分为图上状态转移DAG最小路径覆盖二分图不相交路径等问题

一:【P2754 [CTSC1999] 家园 / 星际转移问题】

1.【题面】

2.【解法】

按照时间分层,跑最大流

3.【代码】

#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=1e5+10,M=100,S=0,T=N-1;int n;
vector<int> g[M];int num[M],up[M];
int bcj[M];
int find(int x){
	if(x==bcj[x]) return x;
	return bcj[x]=find(bcj[x]);
}
void merge(int a,int b){
	a=find(a);b=find(b);
	if(a==b) return ;
	bcj[a]=b;
}
int h[N],to[N],w[N],ne[N],idx=1;
void add(int a,int b,int c){
	to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
	to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int dep[N],now[N];
bool bfs(){
	for(int i=0;i<N;i++) dep[i]=-1;
	for(int i=0;i<N;i++) now[i]=h[i];
	queue<int> q;
	q.push(S);dep[S]=1;
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dep[v]==-1){
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	}
	return dep[T]!=-1;
}
int dfs(int u,int las){
	if(u==T) return las;
	int ans=0;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&dep[u]+1==dep[v]){
			int k=dfs(v,min(las,w[i]));
			w[i]-=k;
			w[i^1]+=k;
			ans+=k;
			las-=k;
		}
	}
	if(ans==0) dep[u]=-1;
	return ans;
}
int Dinic(){
	int ans=0;
	while(bfs()) ans+=dfs(S,inf);
	return ans;
}
int fc(int t,int x){
	return t*n+x;
}
int main(){
	int m,K;cin>>n>>m>>K;
	for(int i=0;i<M;i++) bcj[i]=i;
	for(int i=1;i<=m;i++){
		cin>>up[i];
		int r;cin>>r;
		num[i]=r;
		while(r--){
			int k;cin>>k;
			if(k==0) k=n+1;
			if(k==-1) k=n+2;
			if(g[i].size()) merge(g[i].back(),k);
			g[i].push_back(k);
		}
	}
	n+=2;
	if(find(n-1)!=find(n)){
		cout<<0<<"\n";
		return 0;
	}
	add(S,fc(0,n-1),inf);add(fc(0,n),T,inf);
	
	int Time=0;
	int ans=0;
	while(1){
		for(int i=fc(Time,1);i<=fc(Time,n);i++) add(i,i+n,inf);
		for(int i=1;i<=m;i++){
			int a=g[i][Time%num[i]];
			int b=g[i][(Time+1)%num[i]];
			add(fc(Time,a),fc(Time+1,b),up[i]);
		}
		add(S,fc(Time+1,n-1),inf);add(fc(Time+1,n),T,inf);
		
		Time++;
		ans+=Dinic();
		if(ans>=K){
			cout<<Time<<"\n";
			return 0;
		}
	}
	return 0;
}

二:【P2762 太空飞行计划问题】

1.【题面】

2.【解法】

(1)【转化】

选实验,必须选对应仪器;不选实验,可以不选对应仪器
转化得到,不放弃实验,必须选对应仪器;放弃实验,可以不选对应仪器
继续转化,当实验取 \(0\) (不放弃),所有对应的仪器都要取 \(1\) ;实验取 \(1\) (放弃),对应的仪器取值任意

得到总消耗 \(res\)
用所有实验的总收入 \(all\) 减去 \(res\) 得到 \(ans\) 就是一组解
使 \(ans\) 最大,\(res\) 需要最小,最小割模型

(2)【建图】

\(s\) 向每个实验连容量为 \(p\) 的边,每个实验向对应仪器连容量 \(inf\) 的边,每个仪器向 \(t\) 连容量为 \(c\) 的边

(3)【补充】

把题目抽象出来,得到

给定一张图,有左侧的点和右侧的点,左侧的点点权为正(对应试验),右侧的点点权为负(对应器材),如果选择了左侧的某个点就必须要选右边的一部分点。要求最大化点权和。

如果将左侧的点和右侧的点之间对应连边,如果该实验要求该器材就连一条边,那么问题就被转化为了这样一个问题:

给定一个有向图,点有点权,选择一个子图,满足子图上如果选择了一个点就必须选择它后继的所有点。最大化点权和。

这一类问题被称为最大权闭合子图问题,都可以用上述最小割解法解决

3.【代码】

#include<bits/stdc++.h>
#define int long long
#define inf 1e18
using namespace std;
const int N=210,M=5010,S=0,T=N-1;
int cap[N];
int h[N],to[M],w[M],ne[M],idx=1;
void add(int a,int b,int c){
	to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
	to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int dep[N],now[N];
bool bfs(){
	for(int i=0;i<N;i++){
		dep[i]=-1;
		now[i]=h[i];
	}
	queue<int> q;q.push(S);dep[S]=1;
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dep[v]==-1){
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	}
	return dep[T]!=-1;
}
int dfs(int u,int las){
	if(u==T) return las;
	int ans=0;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&dep[u]+1==dep[v]){
			int res=dfs(v,min(las,w[i]));
			ans+=res;
			las-=res;
			w[i]-=res;
			w[i^1]+=res;
		}
	}
	if(ans==0) dep[u]=-1;
	return ans;
}
int Dinic(){
	int ans=0;
	while(bfs()) ans+=dfs(S,inf);
	return ans;
}
signed main(){
	int n,m;cin>>m>>n;
	int sum=0;
	for(int i=1;i<=m;i++){
		cin>>cap[i];
		sum+=cap[i];
		add(S,i,cap[i]);
		
		char tools[10000];
		memset(tools,0,sizeof tools);
		cin.getline(tools,10000);
		int ulen=0,tool;
		while(sscanf(tools+ulen,"%d",&tool)==1){
			add(i,tool+m,inf);
		    if(tool==0) 
		        ulen++;
		    else{
		        while(tool){
		            tool/=10;
		            ulen++;
		        }
		    }
		    ulen++;
		}
	}
	for(int i=1;i<=n;i++){
		int k;cin>>k;
		add(i+m,T,k);
	}
	int ans=Dinic();
	cout<<sum<<" "<<ans<<"\n";
	for(int i=1;i<=m;i++){
		if(dep[i]!=-1) cout<<i<<" ";
	}cout<<"\n";
	for(int i=1;i<=n;i++){
		if(dep[i+m]!=-1) cout<<i<<" ";
	}cout<<"\n";	
	ans=sum-ans;
	cout<<ans<<"\n";
	return 0;
}

4.【补充】

跑完最大流后,\(S\)\(T\) 一定被割成两部分,由于最大流=最小割,所以最小割一定在满流的边构成的集合中
所以存在一组最小割满足 \(dep!=-1\) 的是 \(S\) 部分,否则是 \(T\) 部分

三:【P2764 最小路径覆盖问题】

1.【题意】

2.【解法】

我们先假设有 \(n\) 个路径,每个路径一个点,然后合并路径
我们把每个点 \(u\) 拆成 \(x\) , \(y\) 两部分,\(s\)\(x_u\) 连一条 \(1\) 容量的边,\(y_u\)\(t\) 连一条 \(1\) 容量的边
如果 \(a\)\(b\) 连边,则 \(x_a-1->y_b\)
然后跑网络流,得到最大合并次数
\(n\) 减去即可

3.【证明】

\(x\) 是接入部分,如果 \(x_a->y_b\) 的边流满,则 \(a\) 的后继是 \(b\)
\(y\) 是被接入部分,如果 \(x_a->y_b\) 的边流满,则 \(b\) 的前驱是 \(a\)
一个点,最多有一个后继,一个前驱,所有边容量都为 \(1\)
跑最大流可以得到最大匹配数即合并次数

4.【代码】

#include<bits/stdc++.h>
#define X(a) (a)
#define Y(b) (b+n)
#define inf 1e9
using namespace std;
const int N=510,M=2e5+10,S=0,T=N-1;
int h[N],to[M],w[M],ne[M],idx=1;
void add(int a,int b,int c){
	to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
	to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int dep[N],now[N];
bool bfs(){
	for(int i=0;i<N;i++){
		dep[i]=-1;
		now[i]=h[i];
	}
	queue<int> q;q.push(S);dep[S]=1;
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dep[v]==-1){
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	}
	return dep[T]!=-1;
}
int dfs(int u,int las){
	if(u==T) return las;
	int ans=0;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&dep[u]+1==dep[v]){
			int res=dfs(v,min(las,w[i]));
			ans+=res;
			las-=res;
			w[i]-=res;
			w[i^1]+=res;
		}
	}
	if(ans==0) dep[u]=-1;
	return ans;
}
int Dinic(){
	int ans=0;
	while(bfs()) ans+=dfs(S,inf);
	return ans;
}
int las[N];
struct Edg{
	int a,b;
}edg[M];
int ind[N];
void dfs(int x){
	if(las[x]) dfs(las[x]);
	cout<<x<<" ";
}
int main(){
	int n,m;cin>>n>>m;
	for(int i=1;i<=m;i++){
		int a,b;cin>>a>>b;
		add(X(a),Y(b),1);
		edg[i]={a,b};
	}
	for(int i=1;i<=n;i++){
		add(S,X(i),1);
		add(Y(i),T,1);
	}
	int ans=n-Dinic();
	for(int i=1;i<=m;i++){
		if(w[i*2]==0){
			las[edg[i].b]=edg[i].a;
			ind[edg[i].a]++;
		}
	}
	for(int i=1;i<=n;i++){
		if(ind[i]==0){
			dfs(i);
			cout<<"\n";
		}
	}
	cout<<ans<<"\n";
	return 0;
}

四:【P2765 魔术球问题】

1.【题意】

2.【解法】

动态加点【P2764 最小路径覆盖问题】

3.【代码】

#include<bits/stdc++.h>
#define X(i) (i)
#define Y(i) (i+N/2)
#define inf 1e9
using namespace std;
const int N=3e5+10,M=5*N,S=0,T=N-1;
int h[N],to[M],w[M],ne[M],idx=1;
void add(int a,int b,int c){
	to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
	to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int dep[N],now[N];
bool bfs(){
	for(int i=0;i<N;i++){
		dep[i]=-1;
		now[i]=h[i];
	}
	queue<int> q;q.push(S);dep[S]=1;
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dep[v]==-1){
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	}
	return dep[T]!=-1;
}
int dfs(int u,int las){
	if(u==T) return las;
	int ans=0;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&dep[v]==dep[u]+1){
			int k=dfs(v,min(las,w[i]));
			w[i]-=k;
			w[i^1]+=k;
			las-=k;
			ans+=k;
		}
	}
	if(ans==0) dep[u]=-1;
	return ans;
}
int Dinic(){
	int ans=0;
	while(bfs()){
		int flow;
		while(flow=dfs(S,inf)) ans+=flow;
	}
	return ans;
}
int vis[N];
int nxt[N];
struct Edg{
	int a,b;
}edg[M];
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int n;cin>>n;
	int res=0,num=0;
	int idx=0;
	while(num-res<=n){
		num++;
		for(int i=sqrt(num);i*i<N;i++){
			int b=i*i-num;
			if(b<=0) continue;
			if(b>=num) break;
			add(X(b),Y(num),1);
			edg[++idx]={b,num};
		}
		add(S,X(num),1);
		edg[++idx]={-1,-1};
		add(Y(num),T,1);
		edg[++idx]={-1,-1};
		res+=Dinic();
	}
	cout<<num-1<<"\n";
	for(int i=1;i<=idx;i++){
		if(edg[i].a==-1) continue;
		int a=edg[i].a,b=edg[i].b;
		if(w[i*2]==0){
			nxt[a]=b;
		}
	}
	for(int i=1;i<=num-1;i++){
		if(vis[i]) continue;
		int x=i;
		while(x&&!vis[x]){
			vis[x]=1;
			cout<<x<<" ";
			x=nxt[x];
		}cout<<"\n";
	}
	return 0;
}

五:【P2763 试题库问题】

1.【题意】

2.【解法】

\(s\)每个试题连容量为 \(1\) 的边,每个类型\(t\) 连容量为所包含题数的边,试题所属类型连容量为 \(1\) 的边
跑最大流

3.【代码】

#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=1e4+10;
int S=0,T;
int h[N],to[N],w[N],ne[N],idx=1;
void add(int a,int b,int c){
	to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
	to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int dep[N],now[N];
bool bfs(){
	for(int i=S;i<=T;i++){
		dep[i]=-1;
		now[i]=h[i];
	}
	queue<int> q;q.push(S);dep[S]=1;
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dep[v]==-1){
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	}
	return dep[T]!=-1;
}
int dfs(int u,int las){
	if(u==T) return las;
	int ans=0;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&dep[v]==dep[u]+1){
			int k=dfs(v,min(las,w[i]));
			w[i]-=k;
			w[i^1]+=k;
			ans+=k;
			las-=k;
		}
	}
	if(ans==0) dep[u]=-1;
	return ans;
}
int Dinic(){
	int ans=0;
	while(bfs()){
		int f;
		while(f=dfs(S,inf)) ans+=f;
	}
	return ans;
}
void print(int u){
	for(int i=h[u];i;i=ne[i]){
		int v=to[i];
		//cout<<u<<" "<<v<<" "<<w[i]<<" "<<i<<"\n";
		if(w[i]) cout<<v<<" ";
	}
}
int main(){
	int K,n;cin>>K>>n;
	T=n+K+1;
	int m=0;
	for(int i=1;i<=K;i++){
		int k;cin>>k;
		m+=k;
		add(n+i,T,k);
	}
	for(int i=1;i<=n;i++){
		add(S,i,1);
		int r;cin>>r;
		while(r--){
			int k;cin>>k;
			add(i,k+n,1);
		}
	}
	int ans=Dinic();
	if(ans!=m){
		cout<<"No Solution!\n";
		return 0;
	}
	for(int i=1;i<=K;i++){
		cout<<i<<": ";
		print(n+i);
		cout<<"\n";
	}
	return 0;
}

六:【P4014 分配问题】

1.【题意】

2.【解法】

这里定义,当 \((a,b)\) 表边权时, \(a\) 为容量, \(b\) 为费用
\(s\) 向每个工作连权值为 \((1,0)\) 的边,每个工作 \(a\) 向所有人 \(b\) 连权值为 \((1,c)\) 的边,每个人向 \(t\) 连权值为 \((1,0)\) 的边
跑费用流

3.【代码】

#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=210,M=N*N;
int S=0,T;
int h[N],to[M],w[M],cost[M],ne[M],idx=1;
void add(int a,int b,int c,int d){
	to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;cost[idx]=d;
	to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;cost[idx]=-d;
}
int mincost;
int dis[N],now[N],vis[N];
bool SPFAmin(){
	for(int i=S;i<=T;i++){
		dis[i]=inf;
		now[i]=h[i];
		vis[i]=0;
	}
	queue<int> q;q.push(S);dis[S]=0;vis[S]=1;
	while(q.size()){
		int u=q.front();q.pop();
		vis[u]=0;
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dis[v]>dis[u]+cost[i]){
				dis[v]=dis[u]+cost[i];
				if(vis[v]) continue;
				vis[v]=1;
				q.push(v);
			}
		}
	}
	return dis[T]!=inf;
}
int dfs(int u,int las){
	if(u==T) return las;
	int ans=0;
	vis[u]=1;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&dis[v]==dis[u]+cost[i]&&!vis[v]){
			int k=dfs(v,min(las,w[i]));
			w[i]-=k;
			w[i^1]+=k;
			ans+=k;
			las-=k;
			mincost+=k*cost[i];
		}
	}
	vis[u]=0;
	if(ans==0) dis[u]=inf;
	return ans;
}
int Dinic(){
	int ans=0;
	while(SPFAmin()){
		int f;
		while(f=dfs(S,inf)) ans+=f;
	}
	return ans;
}
int main(){
	int n;cin>>n;
	T=n+n+1;
	for(int i=1;i<=n;i++){
		add(S,i,1,0);	
		add(i+n,T,1,0);
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			int c;cin>>c;
			add(i,j+n,1,c);
		}
	}
	Dinic();
	cout<<mincost<<"\n";
	for(int i=2;i<=idx;i+=2){
		w[i]+=w[i^1];w[i^1]=0;
		cost[i]=-cost[i];
		cost[i^1]=-cost[i^1];
	}
	mincost=0;
	Dinic();
	cout<<-mincost<<"\n";
	return 0;
}

七:【P2774 方格取数问题】

1.【题意】

2.【解法】

按照横坐标+纵坐标奇偶分点,奇数为白,偶数为黑
\(s\)白点连容量为点权的边,黑点\(t\) 连容量为点权的边
白点相邻黑点连容量 \(inf\) 的边
跑最小割,用总权值减去

3.【代码】

#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=1e4+10,M=4*N;int n,m;
int S=0,T;
int h[N],w[M],to[M],ne[M],idx=1;
void add(int a,int b,int c){
	to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
	to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int cl(int x,int y){
	return (x-1)*m+y;
}
int dep[N],now[N];
bool bfs(){
	for(int i=S;i<=T;i++){
		dep[i]=-1;
		now[i]=h[i];
	}
	queue<int> q;q.push(S);dep[S]=1;
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dep[v]==-1){
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	}
	return dep[T]!=-1;
}
int dfs(int u,int las){
	if(u==T) return las;
	int ans=0;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&dep[v]==dep[u]+1){
			int k=dfs(v,min(las,w[i]));
			w[i]-=k;
			w[i^1]+=k;
			las-=k;
			ans+=k;
		}
	}
	if(ans==0) dep[u]=-1;
	return ans;
}
int Dinic(){
	int ans=0;
	while(bfs()){
		int f;
		while(f=dfs(S,inf)) ans+=f;
	}
	return ans;
}
int main(){
	cin>>n>>m;
	T=n*m+1;
	int ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			int k;cin>>k;
			ans+=k;
			if((i+j)&1) add(S,cl(i,j),k);
			else add(cl(i,j),T,k);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if((i+j)%2==0) continue;
			if(i>1) add(cl(i,j),cl(i-1,j),inf);
			if(i<n) add(cl(i,j),cl(i+1,j),inf);
			if(j>1) add(cl(i,j),cl(i,j-1),inf);
			if(j<m) add(cl(i,j),cl(i,j+1),inf);
		}
	}
	ans-=Dinic();
	cout<<ans<<"\n";
	return 0;
}

八:【P3355 骑士共存问题】

1.【题意】

2.【解法】

同【P2774 方格取数问题】

3.【代码】

#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=4e4+10,M=10*N;int n;
int S=0,T;
int h[N],w[M],to[M],ne[M],idx=1;
void add(int a,int b,int c){
	to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
	to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int cl(int x,int y){
	return (x-1)*n+y;
}
int dep[N],now[N];
bool bfs(){
	for(int i=S;i<=T;i++){
		dep[i]=-1;
		now[i]=h[i];
	}
	queue<int> q;q.push(S);dep[S]=1;
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dep[v]==-1){
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	}
	return dep[T]!=-1;
}
int dfs(int u,int las){
	if(u==T) return las;
	int ans=0;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&dep[v]==dep[u]+1){
			int k=dfs(v,min(las,w[i]));
			w[i]-=k;
			w[i^1]+=k;
			las-=k;
			ans+=k;
		}
	}
	if(ans==0) dep[u]=-1;
	return ans;
}
int Dinic(){
	int ans=0;
	while(bfs()){
		int f;
		while(f=dfs(S,inf)) ans+=f;
	}
	return ans;
}
int vis[N];
int fx[8][2]={{-2,-1},{-1,-2},{2,-1},{-1,2},{-2,1},{1,-2},{2,1},{1,2}};
#define inmap(x,y) (1<=x&&x<=n&&1<=y&&y<=n)
int main(){
	int m;cin>>n>>m;
	T=n*n+1;
	for(int i=1;i<=m;i++){
		int a,b;cin>>a>>b;
		vis[cl(a,b)]=1;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(vis[cl(i,j)]) continue;
			if((i+j)&1) add(S,cl(i,j),1);
			else add(cl(i,j),T,1);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(vis[cl(i,j)]) continue;
			if((i+j)%2==0) continue;
			for(int k=0;k<8;k++){
				int x=i+fx[k][0],y=j+fx[k][1];
				if(!inmap(x,y)) continue;
				if(vis[cl(x,y)]) continue;
				add(cl(i,j),cl(x,y),inf);
			}
		}
	}
	cout<<n*n-m-Dinic()<<"\n";
	return 0;
}

九:【P4016 负载平衡问题】

1.【题意】

2.【解法】

这里定义,当 \((a,b)\) 表边权时, \(a\) 为容量, \(b\) 为费用
\(s\) 向每个仓库连一条 \((a,0)\) 的边,相邻两个仓库之间连一条 \((inf,1)\) 的边,每个仓库向 \(t\) 连一条 \((平均值,0)\) 的边
跑最小费用最大流

3.【代码】

费用流
#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=310,M=1e5+10;
int S,T;
int h[N],to[M],ne[M],w[M],idx=1;
int cost[M];
void add(int a,int b,int c,int d){
	to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;cost[idx]=d;
	to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;cost[idx]=-d;
}
int dis[N],now[N],vis[N];
bool SPFA(){
	for(int i=S;i<=T;i++){
		dis[i]=inf;
		now[i]=h[i];
	}
	queue<int> q;q.push(S);dis[S]=0;vis[S]=1;
	while(q.size()){
		int u=q.front();q.pop();
		vis[u]=0;
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dis[v]>dis[u]+cost[i]){
				dis[v]=dis[u]+cost[i];
				if(vis[v]) continue;
				vis[v]=1;
				q.push(v);
			}
		}
	}
	return dis[T]!=inf;
}
int mincost;
int dfs(int u,int las){
	if(u==T) return las;
	int ans=0;
	vis[u]=1;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&dis[v]==dis[u]+cost[i]&&!vis[v]){
			int k=dfs(v,min(las,w[i]));
			w[i]-=k;
			w[i^1]+=k;
			las-=k;
			ans+=k;
			mincost+=k*cost[i];
		}
	}
	vis[u]=0;
	if(ans==0) dis[u]=inf;
	return ans;
}
int Dinic(){
	int ans=0;
	while(SPFA()){
		int f;
		while(f=dfs(S,inf)) ans+=f;
	}
	return ans;
}
int main(){
	int n;cin>>n;
	T=n+1;
	int sum=0;
	for(int i=1;i<=n;i++){
		int k;cin>>k;
		sum+=k;
		add(S,i,k,0);
	}
	for(int i=1;i<=n;i++){
		int a=i-1;
		if(a==0) a=n;
		int b=i+1;
		if(b==n+1) b=1;
		add(i,a,inf,1);
		add(i,b,inf,1);
	}
	sum/=n;
	for(int i=1;i<=n;i++){
		add(i,T,sum,0);
	}
	Dinic();
	cout<<mincost<<"\n";
	return 0;
}
贪心
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int mp[N],qh[N];
int main(){
	int n;cin>>n;
	int X=0;
	for(int i=1;i<=n;i++){
		cin>>mp[i];
		X+=mp[i];
	}
	X/=n;
	for(int i=1;i<=n;i++) qh[i]=qh[i-1]+mp[i]-X;
	int ans=0;
	sort(qh+1,qh+1+n);
	int C=qh[n/2+1];
	for(int i=1;i<=n;i++) ans+=abs(C-qh[i]);
	cout<<ans<<endl;
	return 0;
}

十:【P4015 运输问题】

1.【题意】

2.【解法】

这里定义,当 \((a,b)\) 表边权时, \(a\) 为容量, \(b\) 为费用
\(s\) 向每个仓库连 \((a,0)\) 的边,每个仓库 \(x\) 向所有商店 \(y\)\((inf,c_{x,y})\) 的边,每个商店向 \(t\)\((b,0)\) 的边
跑最小费用最大流

3.【代码】

#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=510,M=1e5+10;
int S,T;
int h[N],to[M],ne[M],w[M],idx=1;
int cost[M];
void add(int a,int b,int c,int d){
	to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;cost[idx]=d;
	to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;cost[idx]=-d;
}
int dis[N],now[N],vis[N];
bool SPFA(){
	for(int i=S;i<=T;i++){
		dis[i]=inf;
		now[i]=h[i];
	}
	queue<int> q;q.push(S);dis[S]=0;vis[S]=1;
	while(q.size()){
		int u=q.front();q.pop();
		vis[u]=0;
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dis[v]>dis[u]+cost[i]){
				dis[v]=dis[u]+cost[i];
				if(vis[v]) continue;
				vis[v]=1;
				q.push(v);
			}
		}
	}
	return dis[T]!=inf;
}
int mincost;
int dfs(int u,int las){
	if(u==T) return las;
	int ans=0;
	vis[u]=1;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&dis[v]==dis[u]+cost[i]&&!vis[v]){
			int k=dfs(v,min(las,w[i]));
			w[i]-=k;
			w[i^1]+=k;
			las-=k;
			ans+=k;
			mincost+=k*cost[i];
		}
	}
	vis[u]=0;
	if(ans==0) dis[u]=inf;
	return ans;
}
int Dinic(){
	int ans=0;
	while(SPFA()){
		int f;
		while(f=dfs(S,inf)) ans+=f;
	}
	return ans;
}
int main(){
	int m,n;cin>>m>>n;
    T=n+m+1;
	for(int i=1;i<=m;i++){
		int k;cin>>k;
		add(S,i,k,0);
	}
	for(int i=1;i<=n;i++){
		int k;cin>>k;
		add(i+m,T,k,0);
	}
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			int k;cin>>k;
			add(i,j+m,inf,k);
		}
	}
	Dinic();
	cout<<mincost<<"\n";
	mincost=0;
	for(int i=2;i<=idx;i+=2){
		w[i]+=w[i^1];
		w[i^1]=0;
		cost[i]=-cost[i];
		cost[i^1]=-cost[i^1];
	}
    Dinic();
	cout<<-mincost<<"\n";
	return 0;
}

十一:【P2766 最长不下降子序列问题】

1.【题意】

2.【解法】

(1)【第一问】

\(dp\) 求解,\(f_i\) 表示以 \(i\) 为末尾最长不下降子序列长度
记最长不下降子序列长度为 \(K\)

(2)【第二问】

把每个点拆成 \(x,y\) 两部分, \(x\) 表被其他点接入, \(y\) 表接入其他点
\(s\) 向每个 \(x_i\) (满足 \(f_i==1\) )连一条容量为 \(1\) 的边,每个 \(x_i\)\(y_i\) 连一条容量为 \(1\) 的边,每个 \(y_i\) (满足 \(f_i==K\) )向 \(t\) 连一条容量为 \(1\) 的边
对于二元组 \((i,j)\) 满足 \(num_i \le num_j且f_i+1==f_j\) ,则 \(y_i -1-> x_j\)
跑最大流

(3)【第三问】

如果存在
则把边 \((s,x_1),(x_1,y_1),(x_n,y_n),(y_n,t)\) 的容量改为 \(inf\)
跑最大流

3.【代码】

#include<bits/stdc++.h>
#define X(i) (i)
#define Y(i) (i+n)
#define inf 1e9
using namespace std;
const int N=2010,M=1e5+10;
int S,T;
int a[N],dp[N];

int h[N],to[M],w[M],ne[M],idx=1;
void add(int a,int b,int c){
	to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
	to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int dep[N],now[N];
bool bfs(){
	for(int i=S;i<=T;i++){
		dep[i]=-1;
		now[i]=h[i];
	}
	queue<int> q;q.push(S);dep[S]=1;
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dep[v]==-1){
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	}
	return dep[T]!=-1;
}
int dfs(int u,int las){
	if(u==T) return las;
	int ans=0;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&dep[v]==dep[u]+1){
			int k=dfs(v,min(las,w[i]));
			w[i]-=k;
			w[i^1]+=k;
			las-=k;
			ans+=k;
		}
	}
	if(ans==0) dep[u]=-1;
	return ans;
}
int Dinic(){
	int ans=0;
	while(bfs()){
		int f;
		while(f=dfs(S,inf)) ans+=f;
	}
	return ans;
}
int main(){
	int n;cin>>n;
	T=n+n+1;
	for(int i=1;i<=n;i++) cin>>a[i];
	int K=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++){
			if(a[j]<=a[i]) dp[i]=max(dp[i],dp[j]);
		}
		dp[i]+=1;
		K=max(dp[i],K);
	}
	cout<<K<<"\n";
	int cg1=-1,cg2=-1,cg3=-1,cg4=-1;
	for(int i=1;i<=n;i++){
		add(X(i),Y(i),1);
		if(i==1) cg2=idx-1;
		if(i==n) cg3=idx-1;
		
		if(dp[i]==1){
			add(S,X(i),1);
			if(i==1) cg1=idx-1;
		}
		if(dp[i]==K){
			add(Y(i),T,1);
			if(i==n) cg4=idx-1;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			if(a[i]<=a[j]&&dp[i]+1==dp[j]) add(Y(i),X(j),1);
		}
	}
	cout<<Dinic()<<"\n";
	for(int i=2;i<=idx;i+=2){
		w[i]+=w[i^1];w[i^1]=0;
	}
	w[cg1]=w[cg2]=w[cg3]=w[cg4]=inf;
	if(n==1) cout<<1<<"\n";
	else cout<<Dinic()<<"\n";
	return 0;
}

十二:【P2770 航空路线问题】

1.【题意】

使只在 \(s\)\(t\) 交的两条路径长度和最大

2.【解法】

这里定义,当 \((a,b)\) 表边权时, \(a\) 为容量, \(b\) 为费用
把每个点拆成入点 \(x\) 和出点 \(y\) ,对于每个 \(i(2 \le i \le n-1)\) ,入点向出点连 \((1,1)\) 的边,对于 \(1\)\(n\) ,入点向出点连 \((2,1)\) 的边
如果 \(a\)\(b\) 连边,则 \(y_a\)\(x_b\)\((1,0)\) 的边
跑最大费用最大流

3.【代码】

#include<bits/stdc++.h>
#define X(i) (i)
#define Y(i) (i+n)
#define inf 1e9
#define stds(i) (i<=n?i:i-n)
using namespace std;
const int N=510,M=1e5+10;int n;
int S,T;
map<int,string> name;
map<string,int> id;
int h[N],to[M],w[M],ne[M],cost[M],idx=1;
void add(int a,int b,int c,int d){
	to[++idx]=b;ne[idx]=h[a];w[idx]=c;h[a]=idx;cost[idx]=d;
	to[++idx]=a;ne[idx]=h[b];w[idx]=0;h[b]=idx;cost[idx]=-d;
}
int dis[N],vis[N],now[N];
bool bfs(){
	for(int i=S;i<=T;i++){
		dis[i]=inf;
		now[i]=h[i];
	}
	queue<int> q;dis[S]=0;q.push(S);vis[S]=1;
	while(q.size()){
		int u=q.front();q.pop();
		vis[u]=0;
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dis[v]>dis[u]+cost[i]){
				dis[v]=dis[u]+cost[i];
				if(vis[v]) continue;
				vis[v]=1;
				q.push(v);
			}
		}
	}
	return dis[T]!=inf;
}
int mincost;
int dfs(int u,int las){
	if(u==T) return las;
	int ans=0;
	vis[u]=1;
	for(int i=now[u];i&&las;i=ne[i]){
		now[u]=i;
		int v=to[i];
		if(w[i]&&dis[v]==dis[u]+cost[i]&&!vis[v]){
			int k=dfs(v,min(las,w[i]));
			w[i]-=k;
			w[i^1]+=k;
			las-=k;
			ans+=k;
			mincost+=k*cost[i];
		}
	}
	vis[u]=0;
	if(ans==0) dis[u]=inf;
	return ans;
}
int Dinic(){
	int ans=0;
	while(bfs()){
		int f;
		while(f=dfs(S,inf)) ans+=f;
	}
	return ans;
}
void dfss(int u){
	if(u!=S&&u!=X(1)&&u!=Y(1)) vis[u]=1;
	if(1<=u&&u<=n) cout<<name[u]<<"\n";
	for(int i=h[u];i;i=ne[i]){
		int v=to[i];
		if(w[i]==0&&stds(u)<=stds(v)){
			dfss(v);
			break;
		}
	}
}
void dfst(int u){
	for(int i=h[u];i;i=ne[i]){
		int v=to[i];
		if(w[i]==0&&!vis[v]&&stds(u)<=stds(v)){
			dfst(v);
			break;
		}
	}
	if(1<=u&&u<=n) cout<<name[u]<<"\n";
}
int main(){
	//freopen("in.txt","r",stdin);
	int m;cin>>n>>m;
	T=n+n+1;
	bool check=0;
	for(int i=1;i<=n;i++){
		string s;cin>>s;
		name[i]=s;
		id[s]=i;
	}
	
	add(S,X(1),2,0);add(Y(n),T,2,0);
	add(X(1),Y(1),2,1);add(X(n),Y(n),2,1);
	for(int i=2;i<n;i++) add(X(i),Y(i),1,1);
	
	
	while(m--){
		string a,b;cin>>a>>b;
		int aa=min(id[a],id[b]),bb=max(id[a],id[b]);
		if(aa==1&&bb==n) check=1;
		add(Y(aa),X(bb),1,0);
	}
	
	for(int i=2;i<=idx;i++) cost[i]=-cost[i];
	Dinic();
	if(w[2]==2){
		cout<<"No Solution!\n";
		return 0;
	}
	else if(w[2]==1){
		if(!check) cout<<"No Solution!\n";
		else cout<<2<<"\n"<<name[1]<<"\n"<<name[n]<<"\n"<<name[1]<<"\n";
		return 0;		
	}
	cout<<-mincost-2<<"\n";
	dfss(S);
	dfst(S);
	return 0;
}

4.【总结】

  • 入点向出点连的边,表示点的属性,本题中表示(可选次数,对答案贡献)
  • 出点向入点连的边,表示边的属性,本题中表示(可选次数,对答案贡献)

十三:【P4013 数字梯形问题】

1.【题意】

2.【解法】

不相交路径
这里定义,当 \((a,b)\) 表边权时, \(a\) 为容量, \(b\) 为费用

(1)【第一问】

拆入点 \(x\) 出点 \(y\) ,入点向出点连 \((1,num)\) 的边
\(a,b\) 有边, \(y_a\)\(x_b\)\((1,0)\) 的边
\(s,t\) ,跑最大费用最大流

(2)【第二问】

\((1,num)\)\((inf,num)\)

(3)【第三问】

\((1,num)\)\((inf,num)\)
\((1,0)\)\((inf,0)\)

3.【代码】

#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=5010,M=1e5+10,Q=30;int n,m;
int h[N],to[M],ne[M],w[M],cost[M],idx=1;
int tag[M];
void add(int a,int b,int c,int d){
	to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;cost[idx]=d;
	to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;cost[idx]=-d;
}
int S,T,step;
#define X(x,y) ((x-1)*(n+m-1)+y)
#define Y(x,y) (X(x,y)+step)
int mincost;
int dis[N],vis[N],now[N];
bool bfs(){
	for(int i=S;i<=T;i++){
		dis[i]=inf;
		now[i]=h[i];
	}
	queue<int> q;q.push(S);dis[S]=0;vis[S]=1;
	while(q.size()){
		int u=q.front();q.pop();
		vis[u]=0;
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dis[v]>dis[u]+cost[i]){
				dis[v]=dis[u]+cost[i];
				if(vis[v]) continue;
				vis[v]=1;
				q.push(v);
			}
		}
	}
	return dis[T]!=inf;
}
int dfs(int u,int las){
	if(u==T) return las;
	int ans=0;
	vis[u]=1;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&dis[v]==dis[u]+cost[i]&&!vis[v]){
			int k=dfs(v,min(las,w[i]));
			w[i]-=k;
			w[i^1]+=k;
			las-=k;
			ans+=k;
			mincost+=k*cost[i];
		}
	}
	vis[u]=0;
	if(ans==0) dis[u]=inf;
	return ans;
}
int Dinic(){
	int ans=0;
	while(bfs()){
		int f;
		while(f=dfs(S,inf)) ans+=f;
	}
	return ans;
}
int main(){
	cin>>m>>n;
	step=X(n,n+m-1);S=0;T=step*2+1;
	int cnt=m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=cnt;j++){
			int k;cin>>k;
			add(X(i,j),Y(i,j),1,k);   tag[idx-1]=1;
			if(i!=n){
				add(Y(i,j),X(i+1,j),1,0);   tag[idx-1]=2;  
				add(Y(i,j),X(i+1,j+1),1,0);   tag[idx-1]=2;
			}
		}
		cnt++;
	}
	for(int i=1;i<=m;i++) add(S,X(1,i),1,0);
	for(int i=1;i<=n+m-1;i++){
        add(Y(n,i),T,1,0);
        tag[idx-1]=1;
    }
    for(int i=2;i<=idx;i++) cost[i]=-cost[i];
	Dinic();
	cout<<-mincost<<"\n";
	
	mincost=0;
	for(int i=2;i<=idx;i+=2){
		w[i]+=w[i^1];w[i^1]=0;
	}
	for(int i=2;i<=idx;i++){
		if(tag[i]==1) w[i]=inf;
	}
	Dinic();
	cout<<-mincost<<"\n";
	
	mincost=0;
	for(int i=2;i<=idx;i+=2){
		w[i]+=w[i^1];w[i^1]=0;
	}
	for(int i=2;i<=idx;i++){
		if(tag[i]==2) w[i]=inf;
	}
	Dinic();
	cout<<-mincost<<"\n";		
	return 0;
}

十四:【P4012 深海机器人问题】

1.【题意】

2.【解法】

这里定义,当 \((a,b)\) 表边权时, \(a\) 为容量, \(b\) 为费用
每个点拆成入点 \(x\) 出点 \(y\) ,入点向出点连 \((inf,0)\) 的边
两个相邻的点 \(a,b\)\(y_a\)\(x_b\)\((1,价值)(inf,0)\) 两条边
建超级源点 \(s\) ,汇点 \(t\)
跑最大费用最大流

3.【代码】

#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=1010,M=1e5+10;int n,m;
#define X(i,j) ((i+1)*(m+1)+j)
#define Y(i,j) (X(i,j)+X(n,m))
int h[N],to[M],ne[M],w[M],idx=1,cost[M];
int S,T;
void add(int a,int b,int c,int d){
	to[++idx]=b;ne[idx]=h[a];h[a]=idx;w[idx]=c;cost[idx]=d;
	to[++idx]=a;ne[idx]=h[b];h[b]=idx;w[idx]=0;cost[idx]=-d;
}
int dis[N],now[N],vis[N];
bool SPFA(){
	for(int i=S;i<=T;i++){
		dis[i]=inf;
		now[i]=h[i];
	}
	queue<int> q;q.push(S);dis[S]=0;vis[S]=1;
	while(q.size()){
		int u=q.front();q.pop();
		vis[u]=0;
		for(int i=h[u];i;i=ne[i]){
			int v=to[i];
			if(w[i]&&dis[v]>dis[u]+cost[i]){
				dis[v]=dis[u]+cost[i];
				if(vis[v]) continue;
				q.push(v);
				vis[v]=1;
			}
		}
	}
	return dis[T]!=inf;
}
int mincost;
int dfs(int u,int las){
	if(u==T) return las;
	int ans=0;
	vis[u]=1;
	for(int i=now[u];i&&las;i=ne[i]){
		int v=to[i];
		now[u]=i;
		if(w[i]&&!vis[v]&&dis[v]==dis[u]+cost[i]){
			int k=dfs(v,min(las,w[i]));
			w[i]-=k;
			w[i^1]+=k;
			las-=k;
			ans+=k;
			mincost+=k*cost[i];
		}
	}
	vis[u]=0;
	if(ans==0) dis[u]=inf;
	return ans;
}
int Dinic(){
	int ans=0;
	while(SPFA()){
		int f;
		while(f=dfs(S,inf)){
			ans+=f;
		}
	}
	return ans;
}
int main(){
	int a,b;cin>>a>>b;
	cin>>n>>m;
	for(int i=0;i<=n;i++){
		for(int j=0;j<m;j++){
			int k;cin>>k;
			add(Y(i,j),X(i,j+1),1,k);
			add(Y(i,j),X(i,j+1),inf,0);
		}
	}
	for(int i=0;i<=m;i++){
		for(int j=0;j<n;j++){
			int k;cin>>k;
			add(Y(j,i),X(j+1,i),1,k);
			add(Y(j,i),X(j+1,i),inf,0);
		}
	}
	for(int i=0;i<=n;i++){
		for(int j=0;j<=m;j++){
			add(X(i,j),Y(i,j),inf,0);
		}
	}
	S=0,T=Y(n,m)+1;
	while(a--){
		int k,x,y;cin>>k>>x>>y;
		add(S,X(x,y),k,0);
	}
	while(b--){
		int k,x,y;cin>>k>>x>>y;
		add(Y(x,y),T,k,0);
	}
	for(int i=2;i<=idx;i++) cost[i]=-cost[i];
	Dinic();
	cout<<-mincost<<"\n";
	return 0;
}
posted @ 2026-01-19 08:42  Aistyr  阅读(21)  评论(0)    收藏  举报