int dfn[N], low[N], id; stack<int> st;//由于是递归,stack要定义在函数外 voidtarjan(int x){//求强连通分量,缩点 low[x] = dfn[x] = ++id;//id表示时间戳,所以每次调用自动累加 st.push(x); book[x] = 1; for (int k = g.head[x]; k != -1; k = g.e[k].link) {//搜索相连节点 int v = g.e[k].to; if (!dfn[v]) {//没搜索过 tarjan(v); low[x] = min(low[x], low[v]);//更新所能到的上层节点 } elseif (book[v]) { low[x] = min(low[x], low[v]);//在队中 找到栈中最上端的节点 //dfn是栈中编号或时间戳,如果s在栈中,则x到栈中最上端节点为dfn[s] } } if (dfn[x] == low[x]) {//找到了一个强联通分量,开始弹栈,直到弹到当前这一个点为止 while (!st.empty()) { sd[st.top()] = x;// 染色,相当于对每一组强连通分支进行编号 book[st.top()] = 0; if (x == st.top()) { st.pop();//即使条件满足也是要出栈的 break; } g.p[x].val += g.p[st.top()].val;//连通分支的值合并(变环为点) st.pop(); } } }
在遍历完之后,还有一个很重要的步骤——存储已经缩点后的新图。
如果原图一条边上两点属于不同的强连通分量,则两个强连通分量相连,就可以对这两个分量连接
1 2 3 4 5 6 7 8 9 10 11
for (int i = 1; i <= m; i++) { int x = sd[g.e[i].from]; int y = sd[g.e[i].to]; if (x != y) {//不属于同一分量(还记得之前的染色吗) ed[++s].link = h[x]; ed[s].to = y; ed[s].from = x; h[x] = s; ind[y]++; }//邻接表存储重新形成新的图 }
#include<bits/stdc++.h> usingnamespace std; #define M 200007 #define N 20007 #define INF 0x3f3f3f3f #define mod 99999997 #define ll long long #define db double #define pii pair<int, int> int n, m; typedefstructedge { int from, to, link; int id, val; } Edge; typedefstructnode { int val; } Node; typedefstructgraph { int head[N]; int node_num = 0; Node p[N]; int edge_num = 0; Edge e[M]; } Graph; Graph g; Edge ed[M];//新图的 int s, h[N];//新图的边数和头结点 int book[N], sd[N]; voidadd_edge(int from, int to){ g.e[++g.edge_num].to = to; g.e[g.edge_num].from = from; g.e[g.edge_num].link = g.head[from]; g.head[from] = g.edge_num; } int dfn[N], low[N],id; stack<int> st; voidtarjan(int x){ low[x] = dfn[x] = ++id; st.push(x); book[x] = 1; for (int k = g.head[x]; k != -1; k = g.e[k].link) { int v = g.e[k].to; if (!dfn[v]) { tarjan(v); low[x] = min(low[x], low[v]); } elseif (book[v]) { low[x] = min(low[x], low[v]); } } if (dfn[x] == low[x]) { while (!st.empty()) { sd[st.top()] = x; book[st.top()] = 0; if (x == st.top()) { st.pop(); break; } g.p[x].val += g.p[st.top()].val; st.pop(); } } } int ind[N], f[N]; inttopological_sort(){ queue<int> q; for (int i = 1; i <= n; i++) { if (ind[i] == 0 && sd[i] == i) { //之后拓扑排序时用sd[i]==i保证这个点存在于新图 q.push(i); f[i] = g.p[i].val; } }; while (!q.empty()) { int rhs = q.front(); q.pop(); for (int i = h[rhs]; i; i = ed[i].link) { int u = ed[i].to; ind[u]--; if (ind[u] == 0) q.push(u); f[u] = max(f[u], f[rhs] + g.p[u].val); } } int ans = 0; for (int i = 1; i <= n; i++) { ans = max(ans, f[i]);//这里用dp维护最大值 cerr << f[i] << endl; } return ans; } intmain(){ ios::sync_with_stdio(false); cout.tie(NULL); memset(g.head, -1, sizeof(g.head)); cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> g.p[i].val; } int u, v; for (int i = 1; i <= m; i++) { cin >> u >> v; add_edge(u, v); } for (int i = 1; i <= n; i++) { if (!dfn[i]) { tarjan(i); } } for (int i = 1; i <= m; i++) { int x = sd[g.e[i].from]; int y = sd[g.e[i].to]; if (x != y) { ed[++s].link = h[x]; ed[s].to = y; ed[s].from = x; h[x] = s; ind[y]++; } } cout << topological_sort(); return0; }