1 条题解

  • 0
    @ 2025-8-24 21:41:20

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar totorato
    **

    搬运于2025-08-24 21:41:20,当前版本为作者最后更新于2017-11-25 10:53:54,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    (不需要利用二分图匹配相关定理)

    分析:

    我们首先将原图用n条路径覆盖,每条边只经过每个节点。

    现在尽量合并更多的路径(即将两个路径通过一条边首尾相连)。

    可以知道,每合并两条路径,图中的路径覆盖数就会减少1。

    所以我们只需要利用网络流合并相关的路径即可。

    答案求解:

    首先将每个节点拆成(Xi,Yi)两个节点,建立源点和汇点,分别连接(S,Xi)和(Yi,T)。

    然后对于每一条原图中的边,建立边(Xi,Yi)即可。

    这样每一条增广路都只会经过2个节点(Xa,Yb),对应合并的两个节点。

    由于每个节点至多与一个节点合并,故边(S,Xi)和(Yi,T)容量为1。

    此时的最大流对应的就是最多可以合并的路径数。

    方案输出:

    由于本人没有想到什么好的输出方法,故只能比较蠢地根据网络流的残余流量构造每一条路径(利用并查集维护路径起点),然后从起点递归输出。

    ``cpp

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #define MX 20001
    #define S 0
    #define T ((n<<1)+1)
    #define oo 12312312
    using namespace std;
    typedef struct edge_t
    {
        int u,v,c;
    }edge;
    edge e[MX];
    int fst[MX],nxt[MX],lnum;
    int n,m;
    void addeg(int nu,int nv,int nc)
    {
        nxt[++lnum]=fst[nu];
        fst[nu]=lnum;
        e[lnum]=(edge){nu,nv,nc};
    }
    void input()
    {
        int a,b;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&a,&b);
            addeg(a,n+b,1);
            addeg(n+b,a,0);
        }
        for(int i=1;i<=n;i++)addeg(S,i,1),addeg(i,S,0);
        for(int i=n+1;i<=n<<1;i++)addeg(i,T,1),addeg(T,i,0);
    }
    void init()
    {
        memset(fst,0xff,sizeof(fst));
        lnum=-1;
    }
    int dep[MX],q[MX];
    int bfs(int frm,int to)
    {
        int x,y,h=0,t=1;
        memset(dep,0xff,sizeof(dep));
        q[++h]=frm;
        dep[frm]=0;
        while(h>=t)
        {
            x=q[t++];
            for(int i=fst[x];i!=-1;i=nxt[i])
            {
                y=e[i].v;
                if(e[i].c&&dep[y]==-1)
                {
                    dep[y]=dep[x]+1;
                    q[++h]=y;
                }
            }
        }
        return (dep[to]>=0);
    }
    int dinic(int to,int x,int mn)
    {
        if(x==to)return mn;
        int a,now=0,y;
        for(int i=fst[x];i!=-1;i=nxt[i])
        {
            y=e[i].v;
            if(e[i].c&&dep[y]==dep[x]+1)
            {
                a=dinic(to,y,min(mn-now,e[i].c));
                now+=a;
                e[i].c-=a;
                e[i^1].c+=a;
                if(now==mn)break;
            }
        }
        return now;
    }
    void output(int x)
    {
        printf("%d ",x);
        for(int i=fst[x];i!=-1;i=nxt[i])
            if(e[i].c==0&&e[i].v>n)
                output(e[i].v-n);
    }
    int fa[MX];
    int findfa(int x){return x==fa[x]?x:fa[x]=findfa(fa[x]);}
    void work()
    {
        int tot=0;
        while(bfs(S,T))tot+=dinic(T,S,+oo);
        for(int i=1;i<=n;i++)fa[i]=i;
        for(int i=0;i<=lnum;i++)
            if(e[i].u>=1&&e[i].u<=n&&e[i].v>n&&e[i].v<T&&e[i].c==0)
                fa[findfa(e[i].v-n)]=findfa(e[i].u);
        for(int i=1;i<=n;i++)
            if(findfa(i)==i)
                output(i),putchar('\n');
        printf("%d\n",n-tot);
    }
    int main()
    {
        init();
        input();
        work();
        return 0;
    }
    
    • 1

    信息

    ID
    1791
    时间
    1000ms
    内存
    128MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者