求有向无环图的不相交最小路径覆盖。
把原图的每个点V拆成Vx和Vy两个点,如果有一条有向边A->B,那么就加边Ax−>By。这样就得到了一个二分图。那么最小路径覆盖=原图的结点数-新图的最大匹配数。
证明:一开始每个点都是独立的为一条路径,总共有n条不相交路径。我们每次在二分图里找一条匹配边就相当于把两条路径合成了一条路径,也就相当于路径数减少了1。所以找到了几条匹配边,路径数就减少了多少。所以有最小路径覆盖=原图的结点数-新图的最大匹配数。
因为路径之间不能有公共点,所以加的边之间也不能有公共点,这就是匹配的定义。
输出路径时,可以从一个点开始一直找他的匹配点输出。
#include
using namespace std;
#define ll long long
#define N 210
#define inf 0x3f3f3f3f
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,m,h[N],num=0,bf[N],ans=0,gf[N];
bool f[N];
struct edge{
int to,next;
}data[6010];
inline bool find(int x){
for(int i=h[x];i;i=data[i].next){
int y=data[i].to;if(f[y]) continue;
f[y]=1;if(!bf[y]||find(bf[y])){bf[y]=x;gf[x]=y;return 1;}
}return 0;
}
int main(){
// freopen("a.in","r",stdin);
n=read();m=read();
while(m--){
int x=read(),y=read();
data[++num].to=y;data[num].next=h[x];h[x]=num;
}for(int i=1;i<=n;++i){
memset(f,0,sizeof(f));if(find(i)) ans++;
}memset(f,0,sizeof(f));
for(int i=1;i<=n;++i){
if(f[i]) continue;int x=i;printf("%d",x);f[x]=1;
while(gf[x]){
x=gf[x];f[x]=1;printf(" %d",x);
}puts("");
}printf("%d\n",n-ans);
return 0;
}