题目
求有向无环图的最小路径覆盖(可以从任意一个点开始)
分析
根据最大流,可得最小路径覆盖=结点数-最大流,所以关键是任意一个点作为起点,所以需要拆点,源点与入点相连,出点与汇点相连,中间就是那些边
代码
#include <cstdio>
#include <queue>
using namespace std;
struct node{int y,w,next;}e[20001];
int n,ans,s,m,t,k=1,ls[301],dis[301],f[301];
int in(){
int ans=0; char c=getchar();
while (c<48||c>57) c=getchar();
while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
return ans;
}
void add(int x,int y,int w){
e[++k]=(node){y,w,ls[x]}; ls[x]=k;
e[++k]=(node){x,0,ls[y]}; ls[y]=k;
}
bool bfs(int s){
for (int i=1;i<=t;i++) dis[i]=0;
queue<int>q; q.push(s); dis[s]=1;
while (q.size()){
int x=q.front(); q.pop();
for (int i=ls[x];i;i=e[i].next)
if (e[i].w>0&&!dis[e[i].y]){
dis[e[i].y]=dis[x]+1;
if (e[i].y==t) return 1;
q.push(e[i].y);
}
}
return 0;
}
int dinic(int x,int now){
if (x==t||!now) return now;
int rest=0,h;
for (int i=ls[x];i;i=e[i].next)
if (e[i].w>0&&dis[e[i].y]==dis[x]+1){
rest+=(h=dinic(e[i].y,min(now-rest,e[i].w)));
e[i].w-=h; e[i^1].w+=h;
if (now==rest) return rest;
}
if (!rest) dis[x]=0;
return rest;
}
int getf(int u){return (f[u]==u)?u:f[u]=getf(f[u]);}
void prin(int ans){if (ans>9) prin(ans/10); putchar(ans%10+48);}
void print(int x){
prin(x); putchar(' ');
for (int i=ls[x];i;i=e[i].next)
if (!e[i].w&&e[i].y>n&&e[i].y<s)
print(e[i].y-n);
}
int main(){
n=in(); m=in(); t=(s=n<<1|1)+1;
for (int i=1;i<=n;i++) add(s,i,1),add(i+n,t,1),f[i]=i;//建图
while (m--) add(in(),in()+n,1);
while (bfs(s)) ans+=dinic(s,400);
for (int i=2;i<=k;i++)
if (e[i].y>n&&e[i].y<s&&e[i^1].y<=n&&!e[i].w){
int fa=getf(e[i^1].y); int fb=getf(e[i].y-n);//并查集维护
int fc=(fa<fb)?fa:fb; f[fc]=fa+fb-fc;//按秩合并+路径压缩
}
for (int i=1;i<=n;i++) if (f[i]==i) print(i),putchar('\n'); //如果找到单独的路径就输出
prin(n-ans);//结点数-最大流
return 0;
}