| Time Limit: 2000MS | Memory Limit: 65536K | |
| Total Submissions: 15755 | Accepted: 4172 | 
Description
Input
Output
Sample Input
1 3 3 1 2 2 3 3 1
Sample Output
Yes
题意:是给出一些点,给出n个点和m条边,接着给出直接相连的边(注意是有向边),求解任意x,y两点间是否存在 x 可到达 y 或者y可
到达x,如果任意x和y都满足这样的条件,就输出"Yes", 否则输出"No".。
注意,这里是 x 到达 y 或者 y 到达 x ,是或者不是而且 !!!
如果是“而且”的话,很明显的是判断整个图是否为一个强连通分量(如题HDU 1296 , 
题目解析),但这题并不是这样。
本题应判断整个图是否为一个弱连通分量。
正确思路:先求解出该有向图的强连通分量,然后根据求解出来的强连通分量进行缩点重新建图
问题转换为求解在新图中是否存在一条能走完所有的顶点的路径,这时可以对缩点后的新图进行拓扑排序,看拓扑排序是否可以成功进行。
拓扑排序遵循条件
一:新图不能有多于1个的入度为0的点,这是保证每个点都有边相连。
二:在拓扑排序遍历点u的过程中,若去掉与u相关的边后出现多于1个的入度为0的点,说明这些点只能由u到达,而它们之间不存在可达路径。这时不满足弱连通,跳出。
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
#define maxn 10000 + 100
#define maxm 100000 + 1000
using namespace std;
int n, m;
struct node {
	int u, v, next;
};
node edge[maxm];
int head[maxn], cnt;
int low[maxn], dfn[maxn];
int dfs_clock;
int Stack[maxn];
bool Instack[maxn];
int top;
int Belong[maxn] , scc_clock;
int in[maxn];
vector<int>Map[maxn];
void init(){
	cnt = 0;
	memset(head, -1, sizeof(head));
}
void addedge(int u, int v){
	edge[cnt] = {u, v, head[u]};
	head[u] = cnt++;
}
void getmap(){
    scanf("%d%d", &n, &m);
    while(m--){
        int a, b;
        scanf("%d%d", &a, &b);
        addedge(a, b);
    }
}
void tarjan(int u, int per){
    int v;
    low[u] = dfn[u] = ++dfs_clock;
    Stack[top++] = u;
    Instack[u] = true;
    for(int i = head[u]; i != -1; i = edge[i].next){
        v = edge[i].v;
        if(!dfn[v]){
            tarjan(v, u);
            low[u] = min(low[v], low[u]);
        }
        else if(Instack[v]){
            low[u] = min(low[u], dfn[v]);
        }
    }
    if(dfn[u] == low[u]){
        scc_clock++;
        do{
            v = Stack[--top];
            Instack[v] = false;
            Belong[v] = scc_clock;
        }while(u != v);
    }
}
void find(){
    memset(low, 0, sizeof(low));
    memset(dfn, 0, sizeof(dfn));
    memset(Instack, false, sizeof(Instack));
    memset(Belong, 0, sizeof(Belong));
    dfs_clock = scc_clock = top = 0;
    for(int i = 1; i <= n; ++i){
        if(!dfn[i])
            tarjan(i, i);
    }
}
void suodian(){
    for(int i = 1; i <= scc_clock; ++i){
        Map[i].clear();
        in[i] = 0;
    }
    for(int i = 0; i < cnt; ++i){
        int u = Belong[edge[i].u];
        int v = Belong[edge[i].v];
        if(u != v){
            Map[u].push_back(v);
            in[v]++;
        }
    }
}
void solve(){
    queue<int>q;
    int num = 0;
    for(int i = 1; i <= scc_clock; ++i){
        if(!in[i]){
            num++;
            q.push(i);
        }
        if(num > 1){
            printf("No\n");
            return ;
        }
    }
    while(!q.empty()){
        int u = q.front();
        q.pop();
        num = 0;
        for(int i = 0; i < Map[u].size(); ++i){
            int v = Map[u][i];
            in[v]--;
            if(!in[v]){
                num++;
                //有两个或两个以上的分支,不是弱连通
                if(num > 1){
                    printf("No\n");
                    return ;
                }
                q.push(v);
            }
        }
    }
    printf("Yes\n");
}
int main (){
    int T;
    scanf("%d", &T);
	while(T--){
        init();
        getmap();
        find();
        suodian();
        solve();
	}
	return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
POJ 2762--Going from u to v or from v to u?【scc缩点新建图 && 判断是否是弱连通图】
原文地址:http://blog.csdn.net/hpuhjh/article/details/47778765