LOJ6002—最小路径覆盖
一道比较友好的网络流?的题了吧
把每个点看做,两个点
如果两个点之间有一条有向边
那就把left向right连一条边
然后做二分图匹配
答案就是总点数减去匹配数
但关键是如何证明这样是正确的
显然最开始的时候每个点是一个独立的集合
每次我们连一条有向边
就相当于把两个点匹配在一起,也就把集合数减少了一个了
那最后集合数就是答案了
#include<bits/stdc++.h>
using namespace std;
int adj[606],nxt[10005],to[10005],lf[10005],rt[10005],vis[10005],ans[10005],cnt,ecnt,n,m;
inline int read(){
char ch=getchar();
int res=0;
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return res;
}
inline void addedge(int u,int v){
nxt[++cnt]=adj[u];adj[u]=cnt,to[cnt]=v;
}
inline int dfs(int u){
for(int e=adj[u];e;e=nxt[e]){
int v=to[e];
if(vis[v]) continue;
vis[v]=1;
if(lf[v]==-1||dfs(lf[v])){
lf[v]=u,rt[u]=v;
return true;
}
}
return false;
}
int main(){
memset(lf,-1,sizeof(lf));
n=read(),m=read();
for(int i=1;i<=m;i++){
int u=read(),v=read();
addedge(u+n,v);
}
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
dfs(i+n);
}
for(int i=1;i<=n;i++){
if(lf[i]==-1){
ecnt++;
cout<<i<<" ";
int u=i+n;
while(rt[u]){
cout<<rt[u]<<" ";
u=n+rt[u];
}
cout<<'\n';
}
}
cout<<ecnt<<'\n';
}