HDOJ 3639 Hawk-and-Chicken

本文介绍了一道经典算法题目的解题思路,利用强连通分量和DFS实现对有向图中可能存在的环进行有效处理。通过Tarjan算法找到所有强连通分量并进行缩点处理后,再进行DFS遍历。

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3639


此题可能拿到的时候很容易想到直接深搜可能就能解决,但是实际上不行,如果要深搜的话,那我们必须要满足一个条件,那就是不能成环,但是这个题显然是可能有成环的(如样例2),那怎么办呢,这时候我们可以去借助强连通分量来解决这个问题,因为如果我们用Tarjan算法把所有的强连通找到后,再讲所有强连通分量缩点处理后,每个点就满足不会成环的性质,我们就可以进行DFS深搜了,当然在这之前还得反向建图求入度(可以不用反向建图求出度),所以总结此题就是强连通+缩点+反向构图+DFS。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#define MAXN 5010 
using namespace std; 
vector<int> g[MAXN],g2[MAXN];
stack<int> st; 
int n,index,num,dfn[MAXN],low[MAXN],belong[MAXN]; 
bool vis[MAXN],instack[MAXN]; 
int ans[MAXN],in[MAXN],scc[MAXN],sum; 
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();
            belong[v]=num;
			scc[num]++;
		}while(v != u); 
        num++;
    }
}
void dfs(int u)//ÉîËÑÇó³ö¿Éµ½´ï¸ÃÁ¬Í¨·ÖÖ§µÄµãÊý
{ 
    vis[u]=true; 
    sum+=scc[u];
    for(int i=0; i<g2[u].size(); i++) 
        if(!vis[g2[u][i]]) 
            dfs(g2[u][i]);
}
int main() 
{
    int T,m,a,b,cas=0; 
    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);
        }
        index=num=0; 
        memset(vis,false,sizeof(vis)); 
        memset(instack,false,sizeof(instack)); 
        memset(scc,0,sizeof(scc)); 
        for(int i=0; i<n; i++) 
            if(!vis[i]) 
                tarjan(i);
        memset(in,0,sizeof(in)); 
        for(int i=0; i<n; i++)
			for(int j=0; j<g[i].size(); j++) 
            {
                if(belong[i] != belong[g[i][j]]) 
                {
                    g2[belong[g[i][j]]].push_back(belong[i]);
                    in[belong[i]]++; 
                }
            }
        int maxans=-1; 
        memset(ans,-1,sizeof(ans)); 
        printf("Case %d: ",++cas); 
        for(int i=0; i<num; i++) 
        {
            if(in[i]==0) 
            {
                sum=0; 
                memset(vis,false,sizeof(vis)); 
                dfs(i);
                ans[i]=sum;
                if(sum>maxans) 
                    maxans=sum;
            }
        }
        printf("%d\n",maxans-1); 
        int flag=0; 
        for(int i=0; i<n; i++) 
            if( ans[belong[i]] == maxans) 
            { 
                if(!flag)
				{
                	printf("%d", i); 
                	flag=1; 
                }
                else printf(" %d",i); 
            }
        printf("\n"); 
    }
    return 0; 
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值