POJ-1236 Tarjan求强连通分量+缩点

一些学校连接到了一个计算机网络。网络中的学校间有如下约定:每个学校维护一个列表,当该学校收到软件或信息后将会转发给列表中的所有学校(也就是接收方列表)。需要注意的是如果B学校在A学校的接收方列表中,A学校不一定会出现在B学校的接收方列表中。
你现在的任务是写出一个程序来计算必须收到一份软件来使网络中的所有学校都能收到软件的学校的数量的最小值(此为子任务A)。作为一个远期目标,我们希望给任意一个学校发送一份软件都能使网络中的所有学校都收到软件。为了实现这个目标,我们或许需要在一些学校的接收方列表中添加新项。 你现在需要计算出至少需要添加多少新项才能实现这个远期目标(此为子任务B)。
Input
第一行是一个整数N:计算机网络中的学校数量(2<=N<=100)。接下来有N行数据,接下来的第 i 行描述了第 i 个学校的接收方列表。每行列表以0结尾。如果第 j 行中只有一个0代表第 j 个学校的接收方列表是空的。
Output
你的程序应该在标准输出中输出两行。每一行应为一个整数:第一行为子任务A的解,第二行为子任务B的解。
Sample Input
5
2 4 3 0
4 5 0
0
0
1 0
Sample Output
1
2

题意
这道题的题意非常清楚 第一个子问题就是求缩点之后入度为零的点 第二个子问题就是求缩点之后的AUG图需要加几条边能成一个强连通图

解题思路
首先这是一道Tarjan的板子题 我们的问题一求解的是缩点之后入度为零的点 这很好理解 因为经过缩点之后图中已经没有环了 成了一个AUG图 那么我们每个入度为零的点均可以遍历自己可以遍历到的点 而不能遍历另一个入度为零的点 因为如果我们可以的话 那就形成了一个环 那不满足AUG图的性质 与我们的条件矛盾。
问题二求解的是一个AUG图加几条边可以形成一个强连通图 结论是出度为零与入度为零的节点的总和的最大值 即max(out_degree,in_degree) 其实就是构造法 对于入度为零的点大于出度为零的点 每一个入度为零的点连接一个出度为零的点 就形成了一个强连通图 对于出度为零的点大于入度为零的点 过程相同

AC代码:

#include<iostream>
#include<algorithm>
#include<string>
#include<map>
#include<set>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<cstdlib>
#include<utility>
#include<cstring>
using namespace std;

int q,m,n,tmp;
vector<int>G[1010];
set<int>se;//记录栈中元素
stack<int>sta;
int id[1010];//缩点
int dfn[1010];
int low[1010];
int in[1010];//入度
int out[1010];//出度
int flag,num,tmpa;
int in_tmp,out_tmp;

int Tarjan(int start)
{
    se.insert(start);
    sta.push(start);
    dfn[start] = low[start] = ++flag;
    int size_for_tmp = G[start].size();
    for(int i=0;i<size_for_tmp;++i){
        int x = G[start][i];
        if(dfn[x] == -1){
            Tarjan(x);
            low[start] = min(low[start],low[x]);
        }else if(se.find(x)!=se.end()){
            low[start] = min(low[start],low[x]);
        }
    }
    if(dfn[start] == low[start]){
        int temp = 0;
        while(1)
        {
            temp = sta.top();
            sta.pop();
            se.erase(temp);
            id[temp] = num;
            if(temp == start) break;
        }
        ++num;
    }
}

int main()
{
    cin >> q;
    for(int i=1;i<=q;i++){
        while(cin >> tmpa && tmpa!=0){
            G[i].push_back(tmpa);
        }
    }
    memset(dfn,-1,sizeof(dfn));
    for(int i=1;i<=q;++i) 
    if(dfn[i]==-1)  Tarjan(i);
    for(int i=1;i<=q;i++){
        int size_for_tmp = G[i].size();
        for(int j=0;j<size_for_tmp;++j){
            int x = G[i][j];
            if(id[x] != id[i]){ //不在一个强连通分量中
                out[id[i]]++;
                in[id[x]]++;
            }
        }
    }
    for(int i=0;i<num;i++){
        if(in[i] == 0) ++in_tmp;
        if(out[i] == 0) ++out_tmp;
    }
    out_tmp = max(in_tmp,out_tmp) ;
    if (num == 1){ //特殊情况 图是一个强连通分量 答案显然就是1和0
        in_tmp = 1;
        out_tmp = 0;
    }
    cout << in_tmp << endl << out_tmp << endl;
    return 0;
}
posted @ 2022-07-02 13:18  李兆龙的博客  阅读(11)  评论(0编辑  收藏  举报