Popular Cows-Tarjan_SCC

update@2011-09-19
C++语言: 高亮代码由发芽网提供

///省略了头文件等等
#define MAX 10010
int N,M;
int head[MAX];
struct edge_node{int u,v,next;}edge[MAX*5];

int component_count;
int belong[MAX];//记录原图中的点属于哪个强连通分量 便于建图
int point_num[MAX];//记录某个缩点中原来的个数
struct edge_node contracted[MAX*5];
int head2[MAX];int edge_count;
void addEdge2(int u,int v){
    contracted[edge_count].u = u;
    contracted[edge_count].v = v;
    contracted[edge_count].next = head2[u];head2[u] = edge_count++;
}
int getNum(){
    int zero_outdegree_sum = 0,id;
   for(int i = 0;i < component_count;i++){
       //遍历所有的缩点 计算出度
       //出度为0的点是满足要求的缩点,返回该缩点中点的个数
       //注意 如果有解 出度为0的点有且只有一个
       int out_degree = 0;
       for(int k = head2[i];k != -1;k = contracted[k].next){
           out_degree++;
       }
       if(out_degree==0){zero_outdegree_sum++;id = i;if(zero_outdegree_sum > 1)return -1;}
   }
   if(zero_outdegree_sum != 1)return -1;
   return point_num[id];
}
void contraction(){
    //将所有强连通分量缩成点 重新建图
    edge_count = 0;//记录新图的边数
    memset(head2,-1,sizeof(head2));

    for(int  i = 1 ;i < N;i++){
        for(int  j = i+1;j <= N;j++){
            if(belong[i] != belong[j]){
                //如果它们不在同一个缩点中
                int flag = 0;
                for(int k = head[i]; k != -1;k = edge[k].next){
                    if(edge[k].v == j){
                        addEdge2(belong[i],belong[j]);flag = 1;break;
                    }
                }
                if(flag)continue;
                for(int k = head[j]; k != -1;k = edge[k].next){
                    if(edge[k].v == i){
                        addEdge2(belong[j],belong[i]);break;
                    }
                }
            }
        }
    }
}

int dfs_order[MAX], lowest[MAX];
int my_stack[MAX],visited[MAX],in_stack[MAX];
int mark_num,top;

void tarjan_scc(int u){
    dfs_order[u] = lowest[u] = mark_num++;
    my_stack[top++] = u;visited[u] = 1;in_stack[u] = 1;

    for(int i = head[u];i != -1;i = edge[i].next){
        int v = edge[i].v;
        if(!visited[v]){
            tarjan_scc(v);
            lowest[u] = mymin(lowest[u],lowest[v]);
        }
        else if(in_stack[v]){
            lowest[u] = mymin(lowest[u],dfs_order[v]);
        }
    }
    if(dfs_order[u] == lowest[u]){
        int p,tota=0;
        do{
            p = my_stack[--top];in_stack[p] = 0;
            belong[p] = component_count; tota++;
        }while(u != p);
        point_num[component_count++] = tota;
    }
}

void addEdge(int u,int v){
    edge[edge_count].u = u;
    edge[edge_count].v = v;
    edge[edge_count].next = head[u];head[u] = edge_count++;
}

///main
int main(){
    while(scanf("%d%d",&N,&M) != EOF){
        edge_count = 0;memset(head,-1,sizeof(head));
        for(int i = 1;i <= M;i++){
            int a,b;scanf("%d%d",&a,&b);
            addEdge(a,b);
        }
        ///init begin
        top = 0; memset(visited,0,sizeof(visited));
        memset(in_stack,0,sizeof(in_stack));
        mark_num = 1;component_count = 0;
        memset(point_num,0,sizeof(point_num));
        ///init end
        for(int i = 1;i <= N;i++){
            if(visited[i]) continue;
            tarjan_scc(i);
        }
        contraction();//缩点操作
        int t = getNum();
        if(t==-1)printf("0\n");
        else printf("%d\n",getNum());
    }
    return 0;
}




题目链接:Popular Cows
把tarjan算法弄懂,然后就是求强连通分量,把所有的强连通分量缩成点,找到一个唯一出度为0的点输出缩成该点的以前的点的个数。为了自己理解方便 写的比较繁琐 效率比较低,其实不用重新建图 .....好累啊 不想写了.