[洛谷P3387]【模板】缩点
题目大意:给定一个$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\n", ans);
return 0;
}

浙公网安备 33010602011771号