P2764 最小路径覆盖问题 网络流

题意:

戳这里

分析:

模板题

题意转化一下,\(ans=总点数-最大边数=总点数-最大流\)

我们把每一个点拆成入点和出点两部分,原点向每一个入点连一条边,每一个出点向汇点连一条边,对于原图上存在的一条边 \(x\to y\)\(x\) 的入点向 \(y\) 的出点连一条边,这些边的流量都是 \(1\) 这样跑出来的最大流就是原图上的最大边数

正确性证明:

按照这样的方式建图,每一个点至多和一个点匹配上,而每一个点和另一个点匹配上的时候最大流会增加 \(1\) 这样最大流就等价于匹配的点对数 \(=\) 图上最大边数

另外这个题还很恶心的要输出方案,看题解后发现,可以通过枚举边,将流量为 \(0\) 的正边,\(to\) 并到 \(frm\) 的并查集里,这样每一个并查集的代表元就是路径的一个起点

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
        return x*f;
    }
    
    const int maxn = 305;
    const int maxm = 2e4+5;
    const int inf = 0x3f3f3f3f;
    int head[maxn],dep[maxn],fa[maxn];
    int n,m,ans,cnt=1,st,ed;
    queue<int> q;

    struct edge
    {
        int frm,to,nxt,w;
    }e[maxm];

    void add_edge(int u,int v,int w)
    {
        e[++cnt].to=v;
        e[cnt].frm=u;
        e[cnt].w=w;
        e[cnt].nxt=head[u];
        head[u]=cnt;
    }

    void add(int u,int v,int w)
    {
        add_edge(u,v,w);
		add_edge(v,u,0);
    }
	
	int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
	
    bool bfs()
    {
        for(int i=st;i<=ed;i++) dep[i]=-1;
        dep[st]=0;q.push(st);
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=head[u];i;i=e[i].nxt)
            {
                int v=e[i].to;
                if(e[i].w&&dep[v]==-1)
                {
                    dep[v]=dep[u]+1;
                    q.push(v);
                }
            }
        }
        return dep[ed]!=-1;
    }

    int dfs(int u,int f)
    {
        if(u==ed) return f;
        int used=0,w;
        for(int i=head[u];i;i=e[i].nxt)
        {
            int v=e[i].to;
            if(e[i].w&&dep[v]==dep[u]+1)
            {
                w=dfs(v,min(e[i].w,f-used));
                e[i].w-=w;
                e[i^1].w+=w;
                used+=w;
                if(used==f) return used;
            }
        }
        if(!used) dep[u]=-1;
        return used;
    }

    void dinic()
    {
        while(bfs())
        {
            int fl=dfs(st,inf);
            ans-=fl;
        }
    }
    
    void solve(int u)
    {
    	printf("%d ",u);
    	for(int i=head[u];i;i=e[i].nxt)
    	{
    		if(!e[i].w&&e[i].to>n)
    		{
    			solve(e[i].to-n);
    			return ;
			}
		}
	}
    
    void work()
    {
        int a,b;
        n=read();m=read();ans=n;st=0;ed=2*n+1;
        for(int i=1;i<=m;i++)
        {
            a=read();b=read();
            add(a,b+n,1);
        }
        for(int i=1;i<=n;i++) fa[i]=i,add(st,i,1),add(i+n,ed,1);
        dinic();
        for(int i=2;i<=cnt;i++) if(e[i].frm>=1&&e[i].frm<=n&&e[i].to>n&&e[i].to<ed&&e[i].w==0) fa[find(e[i].to-n)]=find(e[i].frm);
		for(int i=1;i<=n;i++) if(find(i)==i){solve(i);puts("");}
        printf("%d\n",ans);
    }

}

int main()
{
    zzc::work();
    return 0;
}
posted @ 2021-01-02 10:56  youth518  阅读(43)  评论(0编辑  收藏  举报