tarjan算法的第三个应用 求强连通分量
强连通分量我就不具体介绍了
这次的关键数组含义仍然没变 low[u] 仍然还是 u 能到达的最小的 low[v] ( low[v] 又由它最小的 low[v’] 决定
这里有个很关键的点 low[u] == dfn[u] 若以 求割点与桥的tarjan理解 表示 u 的子树的结点中最早能返回到 u,不能访问到u的祖先, 而 u 又必然能访问到 其子树,因此很简容易便能理解 u 及其子树形成了一个最大的连通块 即 强连通分量
而根据 dfs 的 递归与回溯特性 我们以一个栈来存储一个强连通分量 当得到 low[u]==dfn[u] 时可以逐个出栈得到强连通中的所有结点
鉴于强连通的特性 我们可以将算法简单优化一下 比如 不在 连通栈中的结点 我们已经无需去更新它的 low[u]
HDU 1827 Summer Holiday
ac code
这题的话 tarjan算法求一下缩点 并且吧强连通分量中最小的值记录下来 最后计算下入度为0 的点 加上即可(一开始我还想用并查集 还是太嫩了…………
/* ***********************************************************************
> File Name: contest.cpp
> Author: Key
> Mail: keyld777@gmail.com
> Created Time: 2016年11月18日 星期日 20时10分38秒
********************************************************************** */
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 1005;
int n, m, indx, vis_time, ansn, scn, top;
int head[maxn], low[maxn], dfn[maxn], stack[maxn], val[maxn], color[maxn], minval[maxn];
int in[maxn];
bool instack[maxn];
struct node {
int from;
int to;
int next;
// int w;
};
node edge[2 * maxn];
void AddEdge(int u, int v)
{
edge[indx].from = u;
edge[indx].to = v;
edge[indx].next = head[u];
head[u] = indx++;
}
void init()
{
vis_time = indx = ansn = scn = top = 0;
memset(head, -1, sizeof head);
memset(dfn, 0, sizeof dfn);
memset(low, 0, sizeof low);
memset(in,0,sizeof in);
}
void tarjan(int u)
{
int v;
low[u] = dfn[u] = ++vis_time;
instack[u] = true;
stack[++top] = u;
for (int id = head[u]; id != -1; id = edge[id].next) {
v = edge[id].to;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (instack[v])
low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
scn++;
minval[scn]=val[u];
do {
v = stack[top--];
instack[v] = false;
color[v] = scn;
minval[scn]=min(minval[scn],val[v]);
} while (v != u);
}
}
int main()
{
int u, v;
while (scanf("%d %d", &n, &m) != EOF) {
init();
for (int i = 1; i <= n; i++)
scanf("%d", val + i);
for (int i = 0; i < m; i++) {
scanf("%d %d", &u, &v);
AddEdge(u, v);
}
for (int i = 1; i <= n; i++)
if (!dfn[i])
tarjan(i);
for (int i = 0; i < m; i++) {
u = edge[i].from;
v = edge[i].to;
if (color[u] != color[v])
in[color[v]]++;
}
int ans=0,ansn=0;
for(int i=1;i<=scn;i++)
if(in[i]==0){
ansn++;
ans+=minval[i];
}
printf("%d %d\n",ansn,ans);
}
return 0;
}