hdu1827&&hdu2767----Kosaraju算法

/**
*  hdu1827
*  题意:给一个有向图,每个顶点有个权值,求入度为0的顶点的个数及从入度为0的顶点出发,
*  到达图的所有顶点的最小花费和
*  解法:缩点后,从入度为0的顶点在反向图中dfs一遍,求出路径上的顶点的最小花费
**/

#include <cstdio>
#include <cstring>
#include <climits>
#include <iostream>

using namespace std;

int scc[1005];  ///缩图,存强连通块
int dfn[1005];  ///存顶点访问顺序
int scc_root[1005]; ///存强连通分量中的某一个顶点
int ind[1005];  ///缩图,存入度数
bool vis[1005]; ///访问标记
int cost[1005]; ///花费
int sc, cnt, mincc;
int minp, minc;
///sc    对同一个强连通子图中的顶点标记为同一标志
///cnt   -----
///total 把每个强连通子图看成是一个点,统计其他点可到该点的数目
struct Node {
    int u;
    Node *next;
    Node() {}
    Node(int uu, Node *p=NULL) : u(uu), next(p){}
};
struct Graph {
    Node *head;
    Graph() {}
}G[1005], NG[1005];
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) {///建图
    Node *p = new Node(u);
    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);
        }
    }
}
void dfs_three(int k) {///第三次dfs
    vis[k] = true;
    if (mincc > cost[k]) {///访问路径上的最小花费
        mincc = cost[k];
    }
    for (Node *p=NG[k].head; p!=NULL; p=p->next) {
        if (!vis[p->u])  {
            dfs_three(p->u);
        }
    }
}
void 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;
            scc_root[sc] = dfn[i];
            dfs_two(dfn[i]);
        }
    }

    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]) {///不在同一个强连通内
                ++ind[scc[p->u]];
            }
        }
    }

    minc = 0;
    minp = 0;
    for (int i=1; i<=sc; ++i) {
        if (ind[i] == 0) {///对入度为0的点进行第三次dfs
            ++minp;
            mincc = INT_MAX;
            memset(vis, false, sizeof(vis));
            dfs_three(scc_root[i]);
            minc += mincc;
///           minc += cost[scc_root[i]];
        }
    }
}
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 v, u;
        for (int i=1; i<=n; ++i) {
            scanf("%d", &cost[i]);
        }
        while (m--) {
            scanf("%d%d", &v, &u);
            buildG(G, v, u);
            buildG(NG, u, v);
        }
        kosaraju(n);
        deleteG(G, n);
        deleteG(NG, n);

        printf("%d %d\n", minp, minc);
    }
    return 0;
}


/**
* hdu2767
* 题意: 在一个有向图中,问至少加几条边让整个图变成强连通
* 解法:缩点后统计出度为0和入度为0的点的个数,两者取最大值
**/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

int dfn[20005];  ///存顶点访问顺序
int scc[20005];  ///缩图,存强连通块
int ind[20005];  ///缩图,存入度数
int oud[20005];  ///缩图,存出度数
bool vis[20005]; ///访问标记
int cnt, sc;
struct Node {
    int u;
    Node *next;
    Node() {}
    Node(int uu, Node *p=NULL) : u(uu), next(p){}
};
struct Graph {
    Node *head;
    Graph() {}
}G[20005], NG[20005];
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) {///建图
    Node *p = new Node(u);
    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) {
        ind[i] = oud[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]) {///不在同一个强连通内
                ++oud[scc[i]];
                ++ind[scc[p->u]];
            }
        }
    }
    int max1 = 0;
    int max2 = 0;
    for (int i=1; i<=sc; ++i) {
        if (ind[i] == 0) ++max1;
        if (oud[i] == 0) ++max2;
    }

    int ans = max(max1, max2);
    if (sc == 1) ans = 0;
    return ans;
}
void deleteG(Graph g[], int n) {///释放图
    for (int i=1; i<=n; ++i) {
        Node *p = g[i].head;
        if (p != NULL) {
           Node *pt = p->next;
           while (p != NULL) {
               delete p;
               if (pt == NULL) {
                    p = NULL;
                    break;
               }else {
                    p = pt;
                    pt = pt->next;
               }
           }
           g[i].head = NULL;
        }
    }
}
int main(void) {
    int t, n, m;

    scanf("%d", &t);
    while (t--) {
        scanf("%d%d", &n, &m);
        initG(n);
        while (m--) {
            int u, v;
            scanf("%d%d", &v, &u);
            buildG(G, v, u);
            buildG(NG, u, v);
        }

        int ans = kosaraju(n);
        printf("%d\n", ans);

        deleteG(G, n);
        deleteG(NG, n);
    }

    return 0;
}

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