hdu3072&&hdu3639----Kosaraju算法

/**
*  hdu3072
*  题意:给出有向图,每条边有权值,求出强连通分量之间的最小边权,最后边权相加
*  解析:缩图,更新不在同一个强连通分量的最小边权
**/
#include <cstdio>
#include <climits>
#include <cstring>
#include <iostream>

using namespace std;

int scc[50005];  ///缩图,存强连通块
int dfn[50005];  ///存顶点访问顺序
bool vis[50005]; ///访问标记
int ees[50005];
int sc, cnt;
///sc    对同一个强连通子图中的顶点标记为同一标志
///cnt   -----
struct Node {
    int u;
    int w;
    Node *next;
    Node() {}
    Node(int uu, int ww, Node *p=NULL) : u(uu), w(ww), next(p){}
};
struct Graph {
    Node *head;
    Graph() {}
}G[50005], NG[50005];
void initG(int n) {///初始化
    for (int i=1; i<=n; ++i) {
        G[i].head = NULL;
        NG[i].head = NULL;
    }
}
void buildG(Graph g[], int v, int u, int w) {///建图
    Node *p = new Node(u, w);
    p->next = g[v].head;
    g[v].head = p;
}
void dfs_one(int k) {///第一次dfs求顶点访问顺序
    vis[k] = true;
    for (Node *p=G[k].head; p!=NULL; p=p->next) {
        if (!vis[p->u]) {
            dfs_one(p->u);
        }
    }
    dfn[++cnt] = k;///存顶点访问顺序
}
void dfs_two(int k) {///第二次dfs缩图
    scc[k] = sc;     ///同一个强连通分量标记为一样的标志
    vis[k] = true;
    for (Node *p=NG[k].head; p!=NULL; p=p->next) {
        if (!vis[p->u]) {
            dfs_two(p->u);
        }
    }
}
int kosaraju(int n) {
    cnt = 0;
    memset(vis, false, sizeof(vis));
    for (int i=1; i<=n; ++i) {///求顶点访问顺序
        if (!vis[i]) {
            dfs_one(i);
        }
    }
    memset(vis, false, sizeof(vis));
    sc = 0;
    for (int i=n; i>0; --i) {///缩图
        if (!vis[dfn[i]]) {
            ++sc;
            dfs_two(dfn[i]);
        }
    }

    for (int i=1; i<=sc; ++i)  {
        ees[i] = INT_MAX;
    }
    for (int i=1; i<=n; ++i) {
        for (Node *p=G[i].head; p!=NULL; p=p->next) {
            if (scc[i] != scc[p->u]) {
                ees[scc[p->u]] = min(ees[scc[p->u]], p->w);///更新强连通分量之间的最小边权
            }
        }
    }
    int sum = 0;
    for (int i=1; i<=sc; ++i) {
        if (ees[i] != INT_MAX)  {///求这些最小边权和
            sum += ees[i];
        }
    }
    return sum;
}
void del(Node *pNode) {
    if (pNode != NULL) {
        del(pNode->next);
        delete pNode;
        pNode = NULL;
    }
}
void deleteG(Graph g[], int n) {///释放图
    for (int i=1; i<=n; ++i) {
        if (g[i].head != NULL) {
            del(g[i].head);
        }
    }
}

int main(void) {
    int n, m;
    while (scanf("%d%d", &n, &m) != EOF) {
        initG(n);
        int u, v, w;
        for (int i=0; i<m; ++i) {
            scanf("%d%d%d", &v, &u, &w);
            buildG(G, v+1, u+1, w);
            buildG(NG, u+1, v+1, w);
        }

        printf("%d\n", kosaraju(n));
        deleteG(G, n);
        deleteG(NG, n);
    }
    return 0;
}



/**
*  hdu3639
*  题意:给出有向图,求出度为0的顶点,到达这些顶点的路径所经过的顶点数最多
*  解析:缩图,建立反向图,从入度为0的顶点dfs,dfs过程中,保存从该点出发,能够访问到的顶点数
**/
#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

struct Node {
    int u;
    Node *next;
    Node() {}
    Node(int uu, Node *p=NULL) : u(uu), next(p){}
};
struct Graph {
    Node *head;
    Graph() {}
}G[5005], NG[5005], NNG[5005];
int scc_vs[5005];///存每个强连通子图的顶点个数
int dfn[5005];   ///顶点访问顺序
int scc[5005];   ///缩图
int ind[5005];   ///入度数
int vvs[5005];   ///缩点,反向图,存其他顶点可到该点的数目
bool vis[5005];  ///访问标记
int sc, cnt, vers, total;
///sc    统计强连通子图的个数和对同一个强连通子图中的顶点染成同一色
///cnt   ----
///vers  统计一个强连通子图的顶点个数
///total 把每个强连通子图看成是一个点,统计其他点可到该点的数目
void initG(int n) {///初始化图
    for (int i=1; i<=n; ++i) {
        G[i].head = NG[i].head = NNG[i].head = NULL;
    }
}
void buildG(Graph g[], int v, int u) {
    Node *p = new Node(u);
    p->next = g[v].head;
    g[v].head = p;
}
void dfs_one(int k) {
    vis[k] = true;
    for (Node *p=G[k].head; p!=NULL; p=p->next) {
        if (!vis[p->u])  {
            dfs_one(p->u);
        }
    }
    dfn[++cnt] = k; ///顶点k入栈
}
void dfs_two(int k) {
    scc[k] = sc;   ///标记
    vis[k] = true;
    ++vers;       ///该强连通子图的顶点数加1
    for (Node *p=NG[k].head; p!=NULL; p=p->next) {
        if (!vis[p->u])  {
            dfs_two(p->u);
        }
    }
}
void dfs_three(int k) {
    vis[k] = 1;
    total += scc_vs[k];
    for (Node *p=NNG[k].head; p!=NULL; p=p->next) {
        if (!vis[p->u])  {
            dfs_three(p->u);
        }
    }
}
int kosaraju(int n) {
    sc = 0;
    cnt = 0;
    memset(vis, false, sizeof(vis));
    for (int i=1; i<=n; ++i) {
        if (!vis[i])  {
            dfs_one(i);
        }
    }
    memset(vis, false, sizeof(vis));
    for (int i=cnt; i>0; --i) {
        if (!vis[dfn[i]]) {
            ++sc;
            vers = 0;
            dfs_two(dfn[i]);
            scc_vs[sc] = vers;
        }
    }

    for (int i=1; i<=sc; ++i) {
        ind[i] = 0;
    }
    for (int i=1; i<=n; ++i) {///缩点反向建图
        for (Node *p=G[i].head; p!=NULL; p=p->next) {
            if (scc[i] != scc[p->u]) {
                buildG(NNG, scc[p->u], scc[i]);
                ++ind[scc[i]];
            }
        }
    }
    for (int i=1; i<=sc; ++i)  {
        vvs[i] = -1;
    }
    int maxx = -1;
    for (int i=1; i<=sc; ++i) {
        if (ind[i] == 0) {///对入度为0的点进行第三次DFS
            total = 0;
            for (int j=1; j<=sc; ++j)  {
                vis[j] = false;
            }
            dfs_three(i);
            vvs[i] = total;
            maxx = max(maxx, vvs[i]);
        }
    }

    return maxx;
}
void del(Node *pNode) {
    if (pNode != NULL) {
        del(pNode->next);
        delete pNode;
        pNode = NULL;
    }
}
void deleteG(Graph g[], int n) {///释放图
    for (int i=1; i<=n; ++i) {
        if (g[i].head != NULL) {
            del(g[i].head);
        }
    }
}
int main(void) {
    int n, m, t, cas = 0;
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d", &n, &m);
        initG(n);
        int v, u;
        for (int i=0; i<m; ++i) {
            scanf("%d%d", &v, &u);
            buildG(G, v+1, u+1);
            buildG(NG, u+1, v+1);
        }
        int ans = kosaraju(n);
        printf("Case %d: %d\n", ++cas, ans - 1);
        int cs = 0;
        for (int i=1; i<=n; ++i) {
            if (vvs[scc[i]] == ans) {
                if (cs != 0)  {
                    printf(" %d", i-1);
                }else  {
                    cs = 1;
                    printf("%d", i-1);
                }
            }
        }
        deleteG(G, n);
        deleteG(NG, n);
        deleteG(NNG, sc);
        printf("\n");
    }
    return 0;
}

你可能感兴趣的:(强连通分量,hdu3072,hdu3639,有向图强连通,Kosaraju算法)