HDU1269迷宫城堡连通分量分解

网址:http://acm.hdu.edu.cn/showproblem.php?pid=1269
为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明可以通过这个通道由A房间到达B房间,但并不说明通过它可以由B房间到达A房间。Gardon需要请你写个程序确认一下是否任意两个房间都是相互连通的,即:对于任意的i和j,至少存在一条路径可以从房间i到房间j,也存在一条路径可以从房间j到房间i。
套着模板把题a了
大概地说明一下算法吧。

算法伪代码如下

tarjan(u)
   {
       DFN[u]=Low[u]=++Index                      // 为节点u设定次序编号和Low初值
       Stack.push(u)                              // 将节点u压入栈中
       for each (u, v) in E                       // 枚举每一条边
         if (v is not visted)                    // 如果节点v未被访问过
            tarjan(v)                            // 继续向下找
             //low[v]是进行tarjan后的值,必须比较大小
            Low[u] = min(Low[u], Low[v]) 
         else if (v in S)                        // 如果节点u还在栈内  //这里比较大小时因为u可能指向多个顶点,指向完一个小的后可能指向一个大的顶点需要比较
            Low[u] = min(Low[u], DFN[v])
       if (DFN[u] == Low[u])                      // 如果节点u是强连通分量的根
             repeat
                  v = S.pop                       // 将v退栈,为该强连通分量中一个顶点
                  print v
            until (u== v)

}
#include<iostream>
#include<string>
#include<stack>
#include<vector>
using namespace std;
#define mem(arr,a) memset(arr,a,sizeof(arr))
#define N 10000+5
int dfn[N], low[N];
int vis[N];
int counts;
int number;
int top;
int n, m;
vector<int>G[N];
void tarjan(int x){
    stack<int>s;
    dfn[x] = low[x] = ++counts;
    vis[x] = 1;
    s.push(x);
    for (int i = 0; i < G[x].size(); i++){
        int t = G[x][i];
        if (!dfn[t]){
            tarjan(t);
            if (low[t] < low[x])low[x] = low[t];
        }
        else if (vis[t] && low[x]>dfn[t])low[x] = dfn[t];
    }
    if (low[x] == dfn[x]){
        number++;
        while (!s.empty()){
            int t = s.top(); s.pop();
            vis[t] = 0;
        }
    }
}
int main(){
    while (cin >> n >> m){
        if (!n&&!m)break;
        mem(vis, 0);
        mem(dfn, 0);
        mem(low, 0);
        number = 0;
        counts = 0;
        for (int i = 1; i <= m; i++){
            int a, b;
            cin >> a >> b;
            G[a].push_back(b);
        }
        for (int i = 1; i <= n; i++){
            if (!dfn[i])tarjan(i);
        }
        if (number == 1)cout << "Yes" << endl;
        else cout << "No" << endl;
        for (int i = 1; i <= n; i++)G[i].clear();
    }
}

这个是我原来用邻接阵写的,超内存了,理论上应该是对的,先存在这里吧。

#include<iostream>
#include<string>
#include<stack>
using namespace std;
#define mem(arr,a) memset(arr,a,sizeof(arr))
#define MAX 200+5
#define N 10000+5
int cost[N][N];
int dfn[N], low[N];
int vis[N];
int counts;
int number;
int top;
int n, m;
void tarjan(int x){
    stack<int>s;
    dfn[x] = low[x] = ++counts;
    vis[x] = 1;
    s.push(x);
    for (int i = 1; i <= n; i++){
        if (cost[x][i]){
            if (!dfn[i]){
                tarjan(i);
                if (low[i] < low[x])low[x] = low[i];
            }
            else {
                if (vis[i] && low[x]>dfn[i])low[x] = dfn[i];
            }
        }
    }
    if (low[x] == dfn[x]){
        number++;
        while (!s.empty()){
            int t = s.top(); s.pop();
            vis[t] = 0;
        }
    }
}
int main(){
    while (cin >> n >> m){
        if (!n&&!m)break;
        mem(cost, 0);
        mem(vis, 0);
        mem(dfn, 0);
        mem(low, 0);
        top = -1;
        number = 0;
        counts = 0;
        for (int i = 1; i <= m; i++){
            int a, b;
            cin >> a >> b;
            cost[a][b] = 1;
        }
        for (int i = 1; i <= n; i++)if (!dfn[i])
        tarjan(i);
        if (number == 1)cout << "Yes" << endl;
        else cout << "No" << endl;
    }
}

加一种写法


#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
#define mem(arr,a) memset(arr,a,sizeof(arr))
#define N 10010  
struct node{
    int v, next;
};
node edge[N * 10];
int dfn[N], low[N];
int vis[N];
int heads[N];
int cnt, top, counts;
int num;
int stack[N];
int n, m;
void add(int a, int b){
    edge[counts].next = heads[a];
    edge[counts].v = b;
    heads[a] = counts;
    counts++;
}

void tarjan(int u){
    dfn[u] = low[u] = ++cnt;
    vis[u] = 1;
    stack[top++] = u;
    for (int i = heads[u]; i != -1; i = edge[i].next){
        int v = edge[i].v;
        if (!dfn[v]){
            tarjan(v);
            if (low[u] > low[v])low[u] = low[v];
        }
        else if (vis[v] && low[u] > dfn[v])low[u] = dfn[v];
    }
    if (low[u] == dfn[u]){
        num++;
        int t;
        do{
            t = stack[--top];
            if (t>0)
                vis[t] = 0;
        } while (t != u&&top >= 0);
    }
}

int main(){
    while (cin >> n >> m){
        if (!n&&!m)break;
        num = cnt = top = counts = 0;
        mem(heads, -1);
        mem(dfn, 0);
        mem(vis, 0);
        for (int i = 1; i <= m; i++){
            int a, b;
            cin >> a >> b;
            add(a, b);
        }
        for (int i = 1; i <= n; i++){
            if (!dfn[i])tarjan(i);
        }
        if (num == 1)cout << "Yes" << endl;
        else cout << "No" << endl;
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值