最小路径覆盖

网络流 最小路径覆盖

网络流24题的第三题(刷题速度堪忧)是不是因为颓废于优化hexo了?

总之,题目连接如下:P2764 最小路径覆盖问题

首先,这道题的就需要好好读一读。感觉写的比较正式,有点难懂,需要理解理解。

题目的标题就是最小路径覆盖。说白了,就是找出一些首尾相接的链,要求这些链要遍历所有的节点。“最小”,就是所有可行方案中路径最少的那种规划。

那么怎么解决妮?

想象一下,假如有一个节点,那个节点下面只有一个子节点,是不是就可以将那个子节点直接与节点合并。这么做并不会影响最优答案。(显而易见,易证)

而且,假如一个节点已经向上合并过一次,就不可能有其它的子节点向上合并到这个节点。因为最小“路径”不允许分叉。

那么也就是说,对于最小路径覆盖的路径,我们可以进行一次点合并,且不会影响答案。合并的点就意味着这是一条路径的一部分。

关于点的合并,可以建一个二分图,左右两端节点,便是图的所有节点。假如图上有一条从u到v的边,就在二分图上连一条从u到v的边。跑一边二分图的最大匹配,就是合并的最优规划。

至于其他的小细节(其实也没有什么细节),写的时候自然就能注意到。

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAXN=205,MAXM=6e3+5;
int n,m,ans;
int edge[MAXM],head[MAXN<<1],nxt[MAXM];
int mx[MAXN],my[MAXN]; bool vis[MAXN];

int read();
void insert(int,int,int);
int magnolia();
int point(int);
void print(int);

int main(){
	freopen("test.in","r",stdin);

	n=read(); m=read();

	for(int i=1;i<=m;++i){
		int u,v;		
		u=read(); v=read();
		insert(u,v,i);
	}

	ans=n-magnolia();
	for(int i=1;i<=n;++i){
		if(!my[i]) print(i);
	}
	cout<<ans;

	return 0;
}

int read(){
	char tmp=getchar(); int sum=0; bool flag=false;
	while(tmp<'0'||tmp>'9'){
		if(tmp=='-') flag=true;
		tmp=getchar();
	}
	while(tmp>='0'&&tmp<='9'){
		sum=(sum<<3)+(sum<<1)+tmp-'0';
		tmp=getchar();
	}
	return flag?-sum:sum;
}

void insert(int from,int to,int id){
	nxt[id]=head[from]; head[from]=id; edge[id]=to;
}

int magnolia(){
	int ans=0;
	for(int i=1;i<=n;++i){
		if(mx[i]) continue;
		memset(vis,false,sizeof(vis));
		ans+=point(i);
	}
	return ans;
}

int point(int u){
	for(int i=head[u];i;i=nxt[i]){
		int v=edge[i]; if(vis[v]) continue;
		vis[v]=true;
		if(!my[v]||point(my[v])){
			mx[u]=v;
			my[v]=u;
			return 1;
		}
	}
	return 0;
}

void print(int u){
	printf("%d ",u);
	if(mx[u]) print(mx[u]);
	else printf("\n");
}

ATTENTION
upd:更高阶版本,请务必阅读“题解 魔术球问题”

posted @ 2020-06-29 23:18  ticmis  阅读(126)  评论(0编辑  收藏  举报