洛谷 P2764 最小路径覆盖问题 (网络流24题)

题目:最小路径覆盖问题

update 2021/11/11 :
用最大流又写了一次。
也是先拆点,S和xi相连,xi和yi相连,yi和T相连,边权都为1。
跑完最大流后,xi->yi的边权若为0,则可说明这两点相连。

#include<bits/stdc++.h>
using namespace std;

#define maxn 300
#define maxm 15000
#define read(x) scanf("%d",&x)
#define inf (1<<30)

struct Edge{
	int x,y,z;
	Edge() {}
	Edge(int _x,int _y,int _z) {x=_x,y=_y,z=_z;}
};

int n,m,s,t;
int h[maxn+5],nxt[maxm+5],cnt=-1;
Edge e[maxm+5];

void addedge(int x,int y,int z) {
	e[++cnt]=Edge(x,y,z);
	nxt[cnt]=h[x];
	h[x]=cnt;
}

int d[maxn+5];
queue<int> que;

int bfs() {
	memset(d,0,sizeof(d));
	d[s]=1;
	que.push(s);
	
	while(!que.empty()) {
		int x=que.front();
		que.pop();
		
		for(int i=h[x];~i;i=nxt[i]) {
			int y=e[i].y,z=e[i].z;
			if(d[y]||z==0) continue;
			d[y]=d[x]+1;
			que.push(y);
		}
	}
	
	return d[t];
}

int cur[maxn+5];

int dfs(int x,int w) {
	if(x==t) return w;
	for(int& i=cur[x];~i;i=nxt[i]) {
		int y=e[i].y,z=e[i].z;
		if(0==z||d[y]!=d[x]+1) continue;
		int Min=dfs(y,min(w,z));
		if(Min>0) {
			e[i].z-=Min,e[i^1].z+=Min;
			return Min;
		}
	}
	return 0;
}

int dinic() {
	int ans=0;
	while(bfs()) {
		for(int i=s;i<=t;i++) cur[i]=h[i];
		while(int x=dfs(s,inf)) ans+=x;
	}
	return ans;
}

int to[maxn+5],fa[maxn+5];

void print() {
	for(int i=0;i<=cnt;i++) {
		int x=e[i].x,y=e[i].y,z=e[i].z;
		if(1<=x&&x<=n&&n+1<=y&&y<=2*n&&z==0) {	//只找从 xi -> yi ,剩余流量为0的边 
			to[x]=y-n,fa[y-n]=to[x];
		}
	}
	
	for(int i=1;i<=n;i++) {
		if(fa[i]==0) {
			int x=i;
			while(x) {
				printf("%d ",x);
				x=to[x];
			}
			printf("\n");
		}
	}
}

int main() {
	memset(h,-1,sizeof(h));
	memset(nxt,-1,sizeof(nxt));
	
	read(n),read(m);
	for(int i=1;i<=m;i++) {
		int x,y;
		read(x),read(y);
		addedge(x,n+y,1);
		addedge(n+y,x,0);
	}
	s=0,t=2*n+1;
	for(int i=1;i<=n;i++) {
		addedge(s,i,1);
		addedge(i,s,0);
		addedge(i+n,t,1);
		addedge(t,i+n,0);
	}
	
	int ans=dinic();
	print();
	printf("%d",n-ans);
	
	return 0;
}

思路:
把每个点拆成两个点,其中一个作为入点,另一个作为出点。
然后对于这个二分图求最大匹配输出。
之前因为int没加返回值,系统开了O2优化RE了好久。

代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

#define maxn 150
#define maxm 6000
#define read(x) scanf("%d",&x);

int n,m;
vector<int> g[2*maxn+5];

void readin() {
	read(n);
	read(m);
	for(int i=1; i<=m; i++) {
		int x,y;
		read(x);
		read(y);
		g[x].push_back(y+n);
	}
}

int use[maxn*2+5],match[maxn*2+5];

int dfs(int x) {
	if(use[x]) return false;
	use[x]=true;
	for(int i=0; i<g[x].size(); i++) {
		int y=g[x][i];
		if(!match[y]||dfs(match[y])) {
			match[x]=y;
			match[y]=x;
			return true;
		}
	}
	return false;
}

void slv() {
	for(int i=1; i<=n; i++) {
		dfs(i);
		memset(use,0,sizeof(use));
	}
}

void print() {
	int ans=0;
	for(int i=1;i<=n;i++) {
		if(use[i]) continue;
		ans++;
		int x=i+n;
		do{
			x-=n;
			use[x]=true;
			printf("%d ",x);
		} while(x=match[x]);
		printf("\n");
	}
	printf("%d\n",ans);
}

int main() {
	readin();
	slv();
	print();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值