思路:
在一个有向图中找出最少的路径,使得路径能够覆盖所有的点。
要找最少的路径可以看成把缩边,这样就变为了求最多有多少条边可以被缩掉
建边:
把每个拆成出和入两个点
所有的出点和源点有一条权为1的边,所有的入点和汇点也有一条权为1的边。在缩边的过程中,一个点出来的边最多只有一条可以被缩,进去的也是。
在原图中有边
i
−
>
j
i->j
i−>j,那么就要建
o
u
t
i
−
>
i
n
j
out_{i}->in_{j}
outi−>inj权为无限的边。
如何输出方案,遍历所有的边,找出有流经过的边,且这个边不含源点和汇点,可以知道这两个点是在一起的,利用并查集处理一下,就能够得到方案了。
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int N=505;
const int MAXN = 1<<26;
struct Edge{
int u,v,c;
int nxt;
}edge[N*N];
int n,m;
int head[N],edn;
int d[N];
int sp,tp;
int to[N];
void add_edge(int u,int v,int c)
{
edge[edn].u=u; edge[edn].v=v; edge[edn].c=c;
edge[edn].nxt=head[u]; head[u]=edn++;
edge[edn].u=v; edge[edn].v=u; edge[edn].c=0;
edge[edn].nxt=head[v]; head[v]=edn++;
}
int bfs()
{
queue <int> q;
memset(d,-1,sizeof(d));
d[sp]=0;
q.push(sp);
while(!q.empty())
{
int cur=q.front();
q.pop();
for(int i=head[cur];i!=-1;i=edge[i].nxt)
{
int v=edge[i].v;
if(d[v]==-1 && edge[i].c>0)
{
d[v]=d[cur]+1;
q.push(v);
}
}
}
return d[tp] != -1;
}
int dfs(int a,int b)
{
int r=0;
if(a==tp)return b;
for(int i=head[a];i!=-1 && r<b;i=edge[i].nxt)
{
int v=edge[i].v;
if(edge[i].c>0 && d[v]==d[a]+1)
{
int x=min(edge[i].c,b-r);
x=dfs(v,x);
to[a]=v;
r+=x;
edge[i].c-=x;
edge[i^1].c+=x;
}
}
if(!r)d[a]=-2;
return r;
}
int dinic(int sp,int tp)
{
int total=0,t;
while(bfs())
{
while(t=dfs(sp,MAXN))
total+=t;
}
return total;
}
int fa[N];
bool vis[N];
int findfa(int x){
return fa[x]=fa[x]==x?x:findfa(fa[x]);
}
int main(){
scanf("%d%d",&n,&m);
memset(head,-1, sizeof(head));
edn=0;
sp=301,tp=302;
for(int i=1;i<=n;i++){
add_edge(sp,i,1);
add_edge(n+i,tp,1);
}
for(int i=1,u,v;i<=m;i++){
scanf("%d%d",&u,&v);
add_edge(u,n+v,MAXN);
}
int ans=n-dinic(sp,tp);
for(int i=1;i<=n;i++){
fa[i]=i;
}
for(int i=0;i<edn;i++){
if(edge[i].u>=1&&edge[i].u<=n&&edge[i].v>=n+1&&edge[i].v<=n+n&&edge[i^1].c){
int fau=findfa(edge[i].u);
int fav=findfa(edge[i].v-n);
if(fau!=fav){
fa[fav]=fau;
}
}
}
for(int i=1;i<=n;i++){
if(vis[i])continue;
vis[i]= true;
printf("%d",i);
for(int j=i+1;j<=n;j++){
if(findfa(j)==findfa(i)){
printf(" %d",j);
vis[j]= true;
}
}
printf("\n");
}
printf("%d\n",ans);
return 0;
}