您的位置:首页 > 其它

|算法讨论|强连通分量Tarjan 学习笔记

2017-03-11 16:56 423 查看
题目

[树形DP, 缩点]BZOJ 2427:缩点后跑树上背包

模板及讲解

参考资料:https://www.byvoid.com/blog/scc-tarjan/

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<vector>
#include<cmath>
#define ms(i,j) memset(i, j, sizeof(i));
using namespace std;
struct node
{
int x;
int y;
};  //图里的边
vector<node> q[205];  //边集
stack<int> s;  //tarjan栈
int n;
int index = 0;  //时间戳
int ans = 0;  //强连通分量个数即答案
int ex[205];  //是否访问过,0=没访问,-1=在栈中,1=访问完毕
int dn[205], low[205];  //dn为时间戳,low[i]为结点i能追溯到的最早栈中元素
int tarjan(int u)
{
dn[u]=low[u]=++index;  //赋值时间戳和low的初值
ex[u]=-1;  //标记在栈中
s.push(u);  //放进栈中
node p;
for (int i=0;i<q[u].size();i++)
{  //枚举每一条边(u,v)
p = q[u][i];
if (ex[p.y]==0) {  //结点没访问过
tarjan(p.y);
low[u] = min(low[u], low[p.y]);  //树枝,即(u,v)没访问过且u->v
} else if (ex[p.y]==-1)  //结点在栈中
{
low[u] = min(low[u], dn[p.y]);  //后向边,即(u,v)中的v在栈中
}
}
if (dn[u]==low[u])  //找到一个强连通分量
{
int e;
do
{
e = s.top();
s.pop();
ex[e] = 1;  //标记访问完毕
} while (u!=e);  //退栈直到u!=e
ans++;
}
}
int main ()
{
scanf("%d", &n);
for (int i=1;i<=n;i++)
{
int a;
scanf("%d", &a);
while (a!=0)
{
node r;
r.x = i, r.y = a;
q[i].push_back(r);
scanf("%d", &a);
}
}
ms(ex, 0);
ms(dn,0);
for (int i=1;i<=n;i++)
if (!dn[i]) tarjan(i);
printf("%d\n", ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: