zoukankan      html  css  js  c++  java
  • [网络流24题] 最小路径覆盖问题

    最小(不相交)路径覆盖问题

    很显然要拆点,那么我们就转化得到了一个二分图匹配问题。一条路径上的每条边 ((u,v)) 对应一个 (u o v') 的匹配。因此只要拆点建立二分图,答案就是原图节点数 - 最大匹配数。

    考虑如何输出方案。对于原图的每个节点,我们记录它的两个对应点在新图中参与匹配的次数。显然每条路径一定是从一个参与次数为 (1) 的点开始到一个参与次数为 (1) 的点结束。那么我们可以每次找一个还没有处理过的参与次数为 (1) 的点开始沿着路径走就可以了,这可以看作一个退化的搜索遍历过程。

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 16384, MAXN = 262144;
    #define reset(x) memset(x,0,sizeof x)
    struct graph
    {
        int n,m,M,S,T,head[N],cur[N],dep[N],gap[N],q[N];
        long long ans;
        struct ed
        {
            int from,to,nxt,val;
        } edge[MAXN];
        void init(int n0,int m0,int S0,int T0)
        {
            n=n0,m=m0,S=S0,T=T0,M=1,reset(gap);
            reset(head),reset(cur),reset(dep),reset(q);
        }
        void make(int u,int v,int w)
        {
            edge[++M]=(ed) {u,v,head[u],w},head[u]=M;
        }
        int dfs(int u,int mx)
        {
            if (u==T)
                return mx;
            int num=0,f;
            for (int &i=cur[u],v; i; i=edge[i].nxt)
                if (dep[v=edge[i].to]==dep[u]-1 && (f=edge[i].val))
                    if (edge[i].val-=(f=dfs(v,min(mx-num,f))), edge[i^1].val+=f, (num+=f)==mx)
                        return num;
            if (!--gap[dep[u]++])
                dep[S]=n+1;
            return ++gap[dep[u]],cur[u]=head[u],num;
        }
        void solve()
        {
            for (int i=1; i<=n; ++i)
                cur[i]=head[i];
            ans=0;
            for (gap[0]=n; dep[S]<=n; ans+=dfs(S,0x7fffffff));
        }
    } g;
    
    int n,m,t1,t2,t3,t4,vis[N],ch[N][2],deg[N];
    
    int main()
    {
        cin>>n>>m;
        g.init(2*n+2,0,2*n+1,2*n+2);
        for(int i=1; i<=m; i++)
        {
            cin>>t1>>t2;
            g.make(t1,t2+n,1);
            g.make(t2+n,t1,0);
        }
        for(int i=1; i<=n; i++)
        {
            g.make(2*n+1,i,1);
            g.make(i,2*n+1,0);
            g.make(n+i,2*n+2,1);
            g.make(2*n+2,n+i,0);
        }
        g.solve();
        for(int i=1; i<=m; i++)
        {
            if(g.edge[2*i].val==0)
            {
                ch[(g.edge[2*i].from-1)%n+1][deg[(g.edge[2*i].from-1)%n+1]++]=(g.edge[2*i].to-1)%n+1;
                ch[(g.edge[2*i].to-1)%n+1][deg[(g.edge[2*i].to-1)%n+1]++]=(g.edge[2*i].from-1)%n+1;
            }
        }
        for(int i=1; i<=n; i++)
        {
            if(vis[i])
                continue;
            if(deg[i]==0)
                cout<<i;
            if(deg[i]==1)
            {
                int p = i;
                cout<<p<<" ";
                vis[p]=1;
                p=ch[p][0];
                while(deg[p]>1)
                {
                    vis[p]=1;
                    cout<<p<<" ";
                    if(vis[ch[p][0]])
                        p=ch[p][1];
                    else
                        p=ch[p][0];
                }
                vis[p]=1;
                cout<<p<<" ";
            }
            cout<<endl;
        }
        cout<<n-g.ans<<endl;
    }
    
  • 相关阅读:
    TSQL循环打印一年中所有的日期(WHILE循环)
    给Table加字段的SQL
    [正则表达式]前台JS得到控件ID (该控件被其它控件包住了)
    1.SQL Server中批量更新Object的Owner 2.附加数据库
    转:动态LINQ的几种方法
    转:查看LINQ生成SQL语句的几种方法
    TrimZero方法
    Oracle关联更新语法(TSQL中的update...from)
    Table之间的空隙或Table与父控件之间的空隙怎么去掉?
    自动完成带来的麻烦
  • 原文地址:https://www.cnblogs.com/mollnn/p/11719148.html
Copyright © 2011-2022 走看看