zoukankan      html  css  js  c++  java
  • 2013长沙网赛 I题 Grand Prix

    比赛的时候没时间去看,赛后也是看了题解才过的。

    题目的意思是,给定二分图,判断哪些边是无用的,也就是一定不会在最大匹配中出现。

    解题思路:

    我们可以先跑一边匹配,因为点比较多,只能用HK来做。

    得到一个最大匹配后,枚举每条边,判断其是否为无用边。

    可以分情况考虑:

      设u,v为边的两端点。

      一、match[u]==v (即 match[v]==u)表示这条边为当前最大匹配的一条边,那肯定是可以用边。如果不是,则转到(2)。

      二、因为match[u]!=v (即 match[v]!=u )那么,可以分一下情况考虑:

        (1)match[u]==-1,也就是u尚未匹配,那么此时让u去匹配v,也一定不会导致最大匹配变小,所以该边为有用边。

        (2)match[v]==-1,思路同上。

        (3)match[u]!=-1 && match[v]!=-1 那么,如果让u匹配v,则会让match[u]和match[v]失去匹配对象,那么我们将得到一个匹配,失去两个匹配。此时,我们分析,只要: 

          1.match[v]到match[u]之间存在一条增广路,使match[v]匹配match[u]。

          2.存在某个点x(与u同为X部),match[x]==-1,能够找到一条增广路到达match[u],使得x匹配match[u]。

          3.存在某个点y(与v同为Y部),match[y]==-1,从match[v]出发,能够找到一条增广路到达y,使得match[v]匹配y。

          那么就可以补偿我们失去的一个匹配。

      解法:

      当然,每次枚举边去搜一次增广路的复杂度是很高的,我们可以通过预处理得到解。

      将原来的边转化,若ui匹配vi,则连边vi->ui,若ui不匹配vi,则ui->vi。

      这样,我们可以通过求强连通分量来判断条件1是否成立。

      bfs预处理出所有match[x]==-1的点能够到达的点判断条件2是否成立。

      将边反转一次,bfs预处理出match[y]==-1的点能够到达的点,判断条件3是否成立。

    #include    <iostream>
    #include    <cstdio>
    #include    <cstring>
    #include    <algorithm>
    #include    <queue>
    
    using namespace std;
    
    #define For(i,forN) for(int i=0;i<(forN);i++)
    #define Rep(i,forN) for(int i=(forN);i>=0;i--)
    #define ForEdge(i,u) for(int i=head[u];i!=-1;i=edge[i].next)
    #define sf  scanf
    #define pf  printf
    #define mp  make_pair
    #define _clr(x,y)   memset(x,y,sizeof(x))
    
    const int Maxn=101000,Maxe=Maxn*10;
    
    int match[Maxn],dis[Maxn],head[Maxn],etot,X,Y;
    int n,m;
    
    struct Edge
    {
        int to,next;
    }edge[Maxe];
    
    void init_edge()
    {
        etot=0;
        _clr(head,-1);
    }
    
    void add_edge(int u,int v)
    {
        edge[etot].to=v;
        edge[etot].next=head[u];
        head[u]=etot++;
    }
    
    bool bfs()
    {
        bool res=false;
        queue < int > q;
        For(i,n)    if(match[i]==-1)    q.push(i);
        _clr(dis,0);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            for(int i=head[u];i!=-1;i=edge[i].next)
            {
                int v=edge[i].to;
                if(!dis[v])
                {
                    dis[v]=dis[u]+1;
                    if(match[v]==-1)
                    res=true;
                    else
                    {
                        dis[match[v]]=dis[v]+1;
                        q.push(match[v]);
                    }
                }
            }
        }
        return res;
    }
    
    bool find(int u)
    {
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(dis[v]==dis[u]+1)
            {
                dis[v]=0;
                if(match[v]==-1 || find(match[v]))
                {
                    match[v]=u;
                    match[u]=v;
                    return true;
                }
            }
        }
        return false;
    }
    
    int HK()
    {
        int res=0;
        _clr(match,-1);
        while(bfs())
        {
            For(i,n)
            if(match[i]==-1 && find(i))
            res++;
        }
        return res;
    }
    
    int E[Maxe][2];
    bool Res[Maxe],vis[Maxn],nice[Maxn],canReach[2][Maxn];
    int bleg[Maxn],stk[Maxn],dfsn[Maxn],low[Maxn],vtime,scc,top;
    
    void tarjanDfs(int u)
    {
        vis[u]=true;    stk[top++]=u;
        dfsn[u]=low[u]=vtime++;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(!vis[v])
            {
                tarjanDfs(v);
                low[u]=min(low[u],low[v]);
            }
            else if(bleg[v]==-1)
                low[u]=min(low[u],dfsn[v]);
        }
        if(dfsn[u]==low[u])
        {
            do
            {
                bleg[stk[--top]]=scc;
            }while(stk[top]!=u);
            scc++;
        }
    }
    
    void Tarjan()
    {
        _clr(vis,false);    _clr(bleg,-1);
        vtime=top=0;
        For(i,n)
        if(!vis[i])
        tarjanDfs(i);
    }
    
    void Visit(int ind)
    {
        _clr(vis,false);    _clr(canReach[ind],false);
        queue < int > q;
        if(ind==0)
        {
            For(i,X)    if(match[i]==-1) q.push(i),vis[i]=true,canReach[ind][i]=true;
        }
        else
        {
            for(int i=X;i<n;i++)    if(match[i]==-1) q.push(i),vis[i]=true,canReach[ind][i]=true;
        }
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=head[u];i!=-1;i=edge[i].next)
            {
                int v=edge[i].to;   canReach[ind][v]=true;
                if(!vis[v]) {vis[v]=true;q.push(v);}
            }
        }
    }
    
    int e[Maxe][2];
    
    void reserveEdge()
    {
        int tm=0;
        For(i,n)
        {
            ForEdge(j,i)
            {
                int v=edge[j].to;
                e[tm][0]=v; e[tm++][1]=i;
            }
        }
        init_edge();
        For(i,tm)
        {
            add_edge(e[i][0],e[i][1]);
        }
    }
    
    
    void solve()
    {
        init_edge();
        For(i,m)
        {
            int u=E[i][0],v=E[i][1];
            if(match[v]==u)
            {
                Res[i]=true;
                add_edge(v,u);
                //cout<<v<<" "<<u<<endl;
            }
            else
            {
                Res[i]=false;
                add_edge(u,v);
               // cout<<u<<" "<<v<<endl;
            }
        }
        n=X+Y;
        Tarjan();
        Visit(0);
        reserveEdge(); Visit(1);
        top=0;
        For(i,m)
        {
            if(!Res[i])
            {
                int u=E[i][0],v=E[i][1];
                if(match[u]==-1 || match[v]==-1 || bleg[v]==bleg[match[u]] || canReach[0][match[u]] || canReach[1][v] ) continue;
                else
                stk[top++]=i;
            }
        }
        pf("%d
    ",top);
        For(i,top)
        {
            if(i)   printf(" ");
            printf("%d",stk[i]);
        }
        puts("");
    }
    
    int CTN(char c)
    {
        if(c>='0' && c<='9')    return c-'0';
        return (int)(c-'A')+10;
    }
    
    int getNum(char *str)
    {
        int u=0;
        For(i,3)    u=u*32+CTN(str[i]);
        return u;
    }
    
    int main()
    {
        char str[20];
        while(~sf("%d%d%d",&X,&Y,&m))
        {
            n=X;
            init_edge();
            For(i,m)
            {
                sf("%s",str);
                int u=getNum(str),v=getNum(str+3)+X;
                add_edge(u,v);
                //cout<<u<<" "<<v<<endl;
                E[i][0]=u,E[i][1]=v;
            }
            HK();
            solve();
        }
        return 0;
    }
  • 相关阅读:
    Leetcode 1489找到最小生成树李关键边和伪关键边
    Leetcode 113 路径总和 II
    hdu 1223 还是畅通工程
    hdu 1087 Super Jumping! Jumping! Jumping!
    hdu 1008 Elevator
    hdu 1037 Keep on Truckin'
    湖工oj 1241 畅通工程
    湖工oj 1162 大武汉局域网
    hdu 2057 A + B Again
    poj 2236 Wireless Network
  • 原文地址:https://www.cnblogs.com/CooCoo/p/3405415.html
Copyright © 2011-2022 走看看