[网络流24题]最小路径覆盖问题

本文探讨了网络流在解决二分图问题中的应用,通过将问题转换为点拆分形式,利用最小路径覆盖原理求解。代码实现展示了从问题理解到具体编程的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

话说五一没怎么写过题,

这样的五一怎么能说是完满的呢。。

于是切了一道网络流

乍一看感觉扯不到网络流上来。。其实这是个二分图

把每个点拆开,变成俩个点

之后如果x和y有边就在x1和y2连一条边流量为1

最小路径覆盖就是总点数减最大流量。

代码:

#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define g getchar()
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
inline ll read(){
	ll x=0,f=1;char ch=g;
	for(;ch<'0'||ch>'9';ch=g)if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=g)x=x*10+ch-'0';
	return x*f;
}
inline void out(ll x){
	int a[25],wei=0;
	if(x<0)putchar('-'),x=-x;
	for(;x;x/=10)a[++wei]=x%10;
	if(wei==0){puts("0");return;}
	for(int j=wei;j>=1;--j)putchar('0'+a[j]);
	putchar('\n');
}
struct re{int v,fl,next;}ed[200001];
int dep[10001],dui[10001],head[10001],to[10001],pd[10001];
int n,m,e=1,ans;
inline void ins(int x,int y){
	ed[++e]=(re){y,1,head[x]};head[x]=e;
	ed[++e]=(re){x,0,head[y]};head[y]=e;
}
bool bfs(){
	int tou=1,wei=1;
	memset(dep,-1,sizeof(dep));
	dui[1]=0;dep[0]=0;
	for(;tou<=wei;++tou){
		int u=dui[tou];
		for(int i=head[u];i;i=ed[i].next)
		if(ed[i].fl&&dep[ed[i].v]==-1){
			dep[ed[i].v]=dep[u]+1;
			dui[++wei]=ed[i].v;
		}
	}
	return dep[n*2+1]!=-1;
}
int dfs(int x,int fl){
	if(x==n*2+1)return fl;
	int used=0;
	for(int i=head[x];i;i=ed[i].next)
	if(dep[ed[i].v]==dep[x]+1){
		int w=dfs(ed[i].v,min(fl-used,ed[i].fl));
		if(w){to[x]=ed[i].v;if(ed[i].v-n>0)pd[ed[i].v-n]=1;}
		used+=w;
		ed[i].fl-=w;
		ed[i^1].fl+=w;
		if(used==fl)return fl;
	}
	if(used==0)dep[x]=-1;
	return used;
}
int dinic(){
	int ans=0;
	for(;bfs();)ans+=dfs(0,inf);
	return n-ans;
}
int main(){
//	freopen("","r",stdin);
//	freopen("","w",stdout);
	n=read();m=read();
	for(int i=1;i<=m;++i){int x=read(),y=read();ins(x,y+n);}
	for(int i=1;i<=n;++i)ins(0,i),ins(i+n,n*2+1);
	ans=dinic();
	for(int i=1;i<=n;++i){
		if(pd[i])continue;
		printf("%d",i);
		int k=i;
		for(;to[k];){
			printf(" %d",to[k]-n);
			k=to[k]-n;
		}
		printf("\n");
	}
	out(ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值