最小路径覆盖
Posted ticmis
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最小路径覆盖相关的知识,希望对你有一定的参考价值。
网络流 最小路径覆盖
网络流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("
");
}
ATTENTION
upd:更高阶版本,请务必阅读“题解 魔术球问题”
以上是关于最小路径覆盖的主要内容,如果未能解决你的问题,请参考以下文章