/** * 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; }