题目大意:给定一个$n$个点$m$条边有向图,第$i$个点有权值$w_i$,求一条路径,使路径经过的点权值之和最大,输出点权和,(多次经过一个点只算一次点权)
题解:$tarjan$缩点+$DP$
卡点:1.多处$i,j$打错
2.要求找到一条路径,看成了终点必须为$n$
C++ Code:
#include <cstdio> #define maxn 10010 #define maxm 100010 struct Graph { int head[maxn], cnt; struct Edge { int to, nxt; } e[maxm << 1]; void add(int a, int b) { e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt; } } G1, G2; int n, m, ans; int ind[maxn], q[maxn], h, t; int f[maxn], w[maxn], dp[maxn]; int DFN[maxn], low[maxn], idx, res[maxn], cnt; int S[maxn], top; bool ins[maxn]; inline int min(int a, int b) {return a < b ? a : b;} inline int max(int a, int b) {return a > b ? a : b;} void tarjan(int rt) { DFN[rt] = low[rt] = ++idx; ins[S[++top] = rt] = true; int v; for (int i = G1.head[rt]; i; i = G1.e[i].nxt) { v = G1.e[i].to; if (!DFN[v]) { tarjan(v); low[rt] = min(low[rt], low[v]); } else if (ins[v]) low[rt] = min(low[rt], DFN[v]); } if (DFN[rt] == low[rt]) { cnt++; do { ins[v = S[top--]] = false; res[v] = cnt; f[cnt] += w[v]; } while (v != rt); } } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", w + i); for (int i = 0; i < m; i++) { int a, b; scanf("%d%d", &a, &b); G1.add(a, b); } for (int i = 1; i <= n; i++) { if (!DFN[i]) tarjan(i); } for (int i = 1; i <= n; i++) { for (int j = G1.head[i]; j; j = G1.e[j].nxt) { int v = G1.e[j].to; if (res[i] != res[v]) G2.add(res[i], res[v]), ind[res[v]]++; } } for (int i = 1; i <= cnt; i++) { if (!ind[i]) q[t++] = i, dp[i] = f[i], ans = max(ans, dp[i]); } t--; while (h <= t) { int u = q[h++]; for (int i = G2.head[u]; i; i = G2.e[i].nxt) { int v = G2.e[i].to; dp[v] = max(dp[v], dp[u] + f[v]); if (!--ind[v]) q[++t] = v, ans = max(ans, dp[v]); } } printf("%d ", ans); return 0; }