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;
}

 

 

posted @ 2012-05-19 20:49  枕边梦  阅读(209)  评论(0编辑  收藏  举报