(其实建图方法已经写在题目里了。)
(这一题是一道匈牙利的裸题,考场上二分图最大匹配仍然建议使用匈牙利(因为在实现复杂度上有巨大优势),我纯粹练一下当前弧优化Dinic。)
首先有一个定理。将一张有向图上的每一个点都拆点成入点和出点,连边后形成一张二分图,那么原图的最小路径覆盖,就是顶点数减去这张二分图的最大匹配。
考虑感性地证明这个定理。
每一次匹配,事实上就意味着,一条路径可以额外走过一个点。这样就减少了一个路径覆盖。
所以,当匹配次数最大的时候,路径覆盖数就最小了。
答案用并查集处理一下输出。
#include<iostream>
#include<cstdio>
#include<queue>
const int INF=0x3f3f3f3f;
inline int Min(int A,int B){
return A<B?A:B;
}
struct ee{
int v;
int w;
int nxt;
}e[20005];
int h[2005],et=-1;
inline void Eadd(int U,int V,int W){
e[++et]=(ee){V,W,h[U]};
h[U]=et;
}
inline void add(int U,int V,int W){
Eadd(U,V,W);
Eadd(V,U,0);
}
int dep[2005],nw[2005],f[2005];
int n,m,s,t;
std::queue<int> q;
inline bool bfs(){
for(int i=1;i<=t;++i){
dep[i]=INF;
nw[i]=h[i];
}
while(!q.empty()){
q.pop();
}
dep[s]=0;
q.push(s);
int p;
while(!q.empty()){
p=q.front();
q.pop();
for(int i=h[p];i>=0;i=e[i].nxt){
if(dep[e[i].v]==INF&&e[i].w){
dep[e[i].v]=dep[p]+1;
q.push(e[i].v);
}
}
}
return dep[t]<INF;
}
inline int dfs(int X,int V){
if(V<=0||X==t){
return V;
}
int cnt=0,val;
for(int i=nw[X];i>=0;i=e[i].nxt){
nw[X]=i;
if(dep[e[i].v]==dep[X]+1){
val=dfs(e[i].v,Min(e[i].w,V));
if(val){
cnt+=val;
V-=val;
e[i].w-=val;
e[i^1].w+=val;
if(V<=0){
break;
}
}
}
}
return cnt;
}
inline int fnd(int X){
for(int i=h[X+n];i>=0;i=e[i].nxt){
if(1<=e[i].v&&e[i].v<=n){
if(e[i].w){
return e[i].v;
}
}
}
return X;
}
inline int fa(int X){
return f[X]^X?f[X]=fa(f[X]):X;
}
inline void prnt(int X){
for(int i=h[X];i>=0;i=e[i].nxt){
if((n+1)<=e[i].v&&e[i].v<=(n<<1)&&!e[i].w){
printf("%d ",e[i].v-n
);
prnt(e[i].v-n);
return;
}
}
}
bool vis[2005];
void init(){
scanf("%d%d",&n,&m);
s=(n<<1)+1,t=(n<<1)+2;
for(int i=1;i<=t;++i){
h[i]=-1;
}
int u,v;
for(int i=1;i<=n;++i){
add(s,i,1);
add(n+i,t,1);
}
for(int i=1;i<=m;++i){
scanf("%d%d",&u,&v);
add(u,n+v,1);
}
int ans=n;
while(bfs()){
ans-=dfs(s,INF);
}
for(int i=1;i<=n;++i){
f[i]=i;
}
for(int i=1;i<=n;++i){
f[i]=fnd(i);
}
for(int i=1;i<=n;++i){
fa(i);
}
for(int i=1;i<=n;++i){
if(f[i]==i){
printf("%d ",i);
prnt(i);
puts("");
}
}
printf("%d\n",ans);
}
int main(){
init();
return 0;
}