zoukankan      html  css  js  c++  java
  • hdu3861 The King’s Problem (强连通+最小路径覆盖)

    题意:题目的意思很清晰,对于一个有向图,将N个点划分成最少的集合个数,同时满足俩个条件:

    1) 任意俩点,若互相可达,则必须在同一个集合中

    2)属于同一个集合的任意俩个点对(u,v),至少存在一条路径,使得v对于u 可达 或者 u 对于v 可达

    分析:对于上述俩个条件,为了简化问题,需要进行缩点,属于同一个强连通分量的点,缩成同一个点,重新构图,可以用tarjan 算法;

    这样,第一个条件就一定满足了,接着只剩下第二个条件了,其实,任意俩点,只要在同一条有向路径上,则可以属于一个集合,,,那么问题就转化为用最小的有向路径去覆盖所有的点(最小路径覆盖数==点数-最大匹配数)

    View Code
    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<stack>
    using namespace std;
    const int N = 5000+10;
    vector<int> g[N],g2[N];
    stack<int> st; 
    int n,dfn[N],low[N],f[N],index; 
    int num,match[N]; 
    bool vis[N],instack[N]; 
    void tarjan(int u)//求强连通分支
    { 
        int v; 
        dfn[u] = low[u] = index++;  
        st.push(u);   
        instack[u] = true; 
        vis[u] = true; 
        for(int i=0; i<g[u].size(); i++) 
        {     
            v = g[u][i];       
            if(!vis[v]) 
            {               
                tarjan(v);       
                low[u] = min(low[u], low[v]);  
            }          
            else if(instack[v]) 
                low[u] = min(low[u], dfn[v]); 
        }    
        if(dfn[u] == low[u]) 
        {             
            do 
            {           
                v = st.top();             
                instack[v] = false; 
                st.pop();         
                f[v]=num;//记录每一个点所在的强连通分支   
            } 
            while(v != u); 
            num++;  
        }
    }
    int path(int s)
    {
        vector<int>::iterator it=g2[s].begin();
        for(;it!=g2[s].end();it++)
        {
            int v=*it;
            if(!vis[v])
            {
                vis[v]=true;
                if(match[v]==-1 || path(match[v]))
                {
                    match[v]=s;
                    return 1;
                }
            }
        }
        return 0;
    }
    int main()
    {
        int T,cas=0,m,a,b;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d %d",&n,&m);
            for(int i=0;i<=n;i++) 
            {
                g[i].clear();
                g2[i].clear();
            }
            while(m--) 
            {
                scanf("%d %d",&a,&b); 
                g[a].push_back(b);
            }
            memset(vis,false,sizeof(vis)); 
            memset(instack,false,sizeof(instack)); 
            index=num=0; 
            for(int i=1;i<=n;i++) 
            {
                if(!vis[i]) 
                    tarjan(i);
            }
            for(int i=1;i<=n;i++) //重新构图
                for(int j=0;j<g[i].size();j++) 
                {
                    if(f[i]!=f[g[i][j]]) 
                    {
                        g2[f[i]].push_back(f[g[i][j]]);
                    }
                }
            memset(match,-1,sizeof(match));
            int ans=0;
            for(int i=0;i<num;i++)//求最大匹配数
            {
                memset(vis,false,sizeof(vis));
                ans+=path(i);
            }
            printf("%d\n",num-ans);
        }
        return 0;
    }
  • 相关阅读:
    2008俱乐部高校行之中南民族大学
    [更新]MSDN中Webcast "WPF中的图形系统系列" 课程预告及反馈
    7月20日 武汉.NET俱乐部在线沙龙!
    2007武汉.NET俱乐部沙龙VS2008、WPF、Silverlight
    MSDN新年第一次WebCast总结
    [评]Microsoft Visual Web Developer 2008 Step by Step, Express Edition
    [老爸创作的歌词]我从瓦砾中站起
    [Expert MS IL Assembler]武汉.NET俱乐部在线沙龙与线下聚会
    2008开年大礼:《Application = Code + Markup》中文版面世
    2009武汉.NET俱乐部活动之黄冈站
  • 原文地址:https://www.cnblogs.com/nanke/p/2509173.html
Copyright © 2011-2022 走看看