/*
*State: POJ1236 164K    32MS    C++    2735B
*题目大意:
*        给定一个n (n<=100)个点的有向图,问:
*      Q1、最少需要选择多少个点,使得从这些点出发能遍历完整个图;
*      Q2、最少需要添加多少条有向边,使得整个图成为连通图;
*解题思路:
*        用tarjan_scc缩点之后求入度为0的节点个数即可Q1,之后再算加最少的
*        边使图构成强连通分量即可。算出度为0跟入度为0的数量的最大值。不管
*        缩点后的图是否有连通分支。
*注意:
*        求加最少边变成强连通,只需算出度为0跟入度为0的数量的最大值即可。
*        无论改图是否有连通分量,都一样,但要注意排除该图本身就是强连通图
*        的可能性
*/
View Code
State: 160K    0MS    C++    2158B
#include <iostream>
#include <vector>
#include <cstring>
#include <cstdio>
using namespace std;

const int MAXN = 105;
vector<int> vec[MAXN];
int dfn[MAXN], low[MAXN], inS[MAXN];
int step, scc, id[MAXN], myS[MAXN], top;

//缩点建有向无环图
vector<int> sccvec[MAXN];
int vst[MAXN];

void addEdge(int u, int v)
{
    vec[u].push_back(v);
}

void init()
{
    top = 0;
    scc = 1;
    step = 0;
    for(int i = 0; i < MAXN; i++)
    {
        vst[i] = 0;
        sccvec[i].clear();
        id[i] = -1;
        inS[i] = 0;
        dfn[i] = low[i] = -1;
        vec[i].clear();
    }
}

void tarjan_scc(int n)
{
    dfn[n] = low[n] = ++step;
    myS[top++] = n;
    inS[n] = 1;
    for(unsigned i = 0; i < vec[n].size(); i++)
    {
        int son = vec[n][i];
        if(dfn[son] == -1)
        {
            tarjan_scc(son);
            low[n] = min(low[n], low[son]);
        }
        else if(inS[son] == 1)
            low[n] = min(low[n], dfn[son]);
    }
    if(dfn[n] == low[n])
    {
        int tmp;
        do
        {
            tmp = myS[--top];
            id[tmp] = scc;
            inS[tmp] = 0;
        }while(top != 0 && tmp != n);
        scc++;
    }
}

void deal_scc(int n, int &sol1, int &sol2)
{
    int in[MAXN] = {0}, out[MAXN] = {0};
    int inTree[MAXN] = {0};
    for(int i = 1; i <= n; i++)
    {
        for(unsigned j = 0; j < vec[i].size(); j++)
        {
            int u = i, v = vec[i][j];
            if(id[u] == id[v])
                continue;
            else
            {
                sccvec[id[u]].push_back(id[v]);
                inTree[id[v]]++;
                in[id[v]]++;
                out[id[u]]++;
            }
        }
    }
    sol1 = 0;
    for(int i = 1; i < scc; i++)
    {
        if(inTree[i] == 0)
            sol1++;
    }
    
    //sol2
    int IN = 0, OUT = 0, other = 0;
    for(int i = 1; i < scc; i++)
    {
        if(in[i] == 0)
            IN++;
        if(out[i] == 0)
            OUT++;
    }
    if(scc <= 2)
        sol2 = 0;
    else
        sol2 = max(IN, OUT);
}

int main(void)
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif

    int n;
    while(scanf("%d", &n) == 1)
    {
        init();
        int u, v;
        for(int i = 1; i <= n; i++)
        {
            while(scanf("%d", &v), v)
            {
                addEdge(i, v);
            }
        }
        for(int i = 1; i <= n; i++)
        {
            if(dfn[i] == -1)
                tarjan_scc(i);
        }
        int sol1, sol2;
        deal_scc(n, sol1, sol2);
        printf("%d\n%d\n", sol1, sol2);
    }
    return 0;
}
posted on 2012-08-16 01:39  cchun  阅读(203)  评论(0编辑  收藏  举报