缩点
缩点可以简化图,去除图里的环,化环为点,简化题意。
算法流程:
①:各类数组的意义如下:
d
f
n
[
]
dfn[]
dfn[] 数组记录 该点是该点的访问时间。
l
o
w
[
]
low[]
low[]数组记录 该点所在的环上最早被访问到的点的时间。
s
t
a
[
]
sta[]
sta[] 统计该环上所有的点。
v
i
s
[
]
vis[]
vis[] 记录该点 是否在栈中。
②:
t
a
r
j
a
n
tarjan
tarjan 是
d
f
s
dfs
dfs 遍历的名称。
每访问一个点,记录该点的 dfn 和 low 的信息,入栈 并 标记 vis 数组。
然后从该点继续遍历:
1.如果下一个点未被访问过,则访问该点并重复,比较 并记录 最小的
l
o
w
low
low 值。 (未被访问过的点的
l
o
w
low
low 值 不一定比当前点大, 因为即将访问的点 可能继续访问会出现环,
l
o
w
low
low 值会记录较小值。)
2.如果下一个点被访问过,则
l
o
w
low
low 值记录 该环上最早被访问点的时间,需要取
m
i
n
min
min 来记录。
③:对于每个已经访问其所有出边的点,判断其 low 值 和 dfn 值是否相同。
1.如果相同,则说明该点是 所在环中的最早被访问的点(该环可以大小为1,自己和自己也看作一个环)。然后当前栈顶所在的点一直记录并弹出,直到弹出自身,期间所有的点一定在同一个环中。需要开一些其他数组,记录每个环内 所有点的归属 或是 其他信息。
2.如果不相同,则说明该点在环内,无事发生,找到 一个符合 上边 “1” 的点时再记录当前环。
④:枚举点及其出边,判断是否在同一个归属的环,不在则两个环连边。
例题代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <stack>
#include <ctime>
#include <queue>
#include <set>
using namespace std;
int n, m;
int head[202020],head1[202020],val[202020],val1[202020];
int dfn[202020],low[202020],sta[202020],col[202020],rec[202020];
bool vis[202020];
struct list
{
int to,nxt;
}e[202020],e1[202020];
int read()
{
int rt = 0, in = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') in = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {rt = rt * 10 + ch - '0'; ch = getchar();}
return rt * in;
}
void add_old_edge(int u, int v)
{
e1[++head1[0]].to = v;
e1[head1[0]].nxt = head1[u];
head1[u] = head1[0];
}
void add_edge(int u, int v)
{
e[++head[0]].to = v;
e[head[0]].nxt = head[u];
head[u] = head[0];
}
void tarjan(int x)
{
dfn[x] = low[x] = ++dfn[0];
vis[x] = true;
sta[++sta[0]] = x;
for(int i = head1[x]; i; i = e1[i].nxt)
if(!dfn[e1[i].to])
{
tarjan(e1[i].to);
low[x] = min(low[x], low[e1[i].to]);
}
else if(vis[e1[i].to])
low[x] = min(low[x], dfn[e1[i].to]);
if(low[x] == dfn[x])
{
++col[0];
while(sta[sta[0]+1] != x)
{
col[sta[sta[0]]] = col[0];
val[col[0]] += val1[sta[sta[0]]];
vis[sta[sta[0]--]] = false;
}
}
}
int dfs(int x)
{
if(rec[x]) return rec[x];
rec[x] = val[x];
int sum = 0;
for(int i = head[x]; i; i = e[i].nxt)
{
if(!rec[e[i].to]) dfs(e[i].to);
sum = max(sum, rec[e[i].to]);
}
rec[x] += sum;
return rec[x];
}
int main()
{
n = read(), m = read();
for(int i = 1; i <= n; i++) val1[i] = read();
for(int i = 1; i <= m; i++)
{
int u = read(), v = read();
add_old_edge(u, v);
}
for(int i = 1; i <= n; i++)
if(!dfn[i])
tarjan(i);
for(int i = 1; i <= n; i++)
for(int j = head1[i]; j; j = e1[j].nxt)
if(col[i] != col[e1[j].to])
add_edge(col[i], col[e1[j].to]);
int ans = 0;
for(int i = 1; i <= col[0]; i++)
if(!rec[i])
ans = max(ans, dfs(i));
printf("%d",ans);
system("pause");
return 0;
}