tarjan
缩点
点击查看代码块
/*
tarjan算法解析:
https://blog.csdn.net/acmmmm/article/details/16361033
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
const int maxm=1e5+10;
int dfn[maxn],low[maxn],ct=0;//dfn[u]为节点u搜索被搜索到时的次序编号(时间戳),
//low[u]为u或u的子树能够追溯到的最早的栈中节点的次序号
struct edge{
int from,v,next;
}e[maxm<<1];
int head[maxn],cnt=0;
int fa[maxn];//fa并查集
int book[maxn],num[maxn],siz=0;//book染色数组,num表示一个强连通分量中点的个数,siz表示强连通分量的个数
int chu[maxn],in[maxn];//点的出度,入度
bool instack[maxn];//标记是否点在栈中
stack<int> st;
int n,m;
int w[maxn];//点的权值
int dp[maxn];
void add(int u,int v){
e[cnt].from=u;
e[cnt].v=v;
e[cnt].next=head[u];
head[u]=cnt++;
}
void tarjan(int u){//tarjan缩点
dfn[u]=low[u]=++ct;
instack[u] = 1;
st.push(u);
for (int i=head[u];~i;i=e[i].next){
int v=e[i].v;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(instack[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
siz++;//强连通分量的个数
while(1){
int x = st.top();
st.pop();
book[x] = siz;//染色,点x属于第siz个强连通分量
num[siz]++;//第siz个强连通分量点的个数+1
instack[x] = 0;//标记其不在栈中了
fa[x] = u;//x的祖先是u,x和u在一个环里面
if(x == u) break;//同一个点就不用再加一遍点权值了
w[u]+=w[x];//点u的权值增加
}
}
}
int topo(){//拓扑排序+dp
queue<int>q;
int tot=0;
for (int i=1;i<=n;i++){
if(fa[i] == i && !in[i]){//当前点入度为0并且他是一个单点,假如到队列中
q.push(i);
dp[i]=w[i];
}
}
while(!q.empty()){
int u=q.front();
q.pop();
for (int i=head[u];~i;i=e[i].next){
int v=e[i].v;
dp[v]=max(dp[v],dp[u]+w[v]);
in[v]--;
if(!in[v]){
q.push(v);
}
}
}
int ans=0;
for (int i=1;i<=n;i++){
ans=max(ans,dp[i]);
}
return ans;
}
void init(){//初始化
for (int i=1;i<=n;i++){
fa[i] = i;
}
memset(dfn,0,sizeof(dfn));
memset(instack,0,sizeof(instack));
memset(head,-1,sizeof(head));
memset(num,0,sizeof(num));
memset(book,0,sizeof(book));
memset(dp,0,sizeof(dp));
cnt=0;ct=0;siz=0;
}
int main(){
init();
cin>>n>>m;
for (int i=1;i<=n;i++){
scanf("%d",&w[i]);
}
for (int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
for (int i=1;i<=n;i++){//缩点
if(!dfn[i]){
tarjan(i);
}
}
//缩点之后重新建图
cnt=0;
memset(head,-1,sizeof(head));
for (int i=0;i<m;i++){//遍历每一条原图的边
int x=fa[e[i].from],y=fa[e[i].v];
if(x!=y){//x点和y点不在一个强联通分量中(不在一个环)
in[y]++;
add(x,y);
}
}
int ans = topo();
printf("%d
",ans);
return 0;
}
割点
点击查看代码块
/*
割点判定:
1、当根有至少两个儿子时它是割顶
2、对于儿子点v, 如果不是根的直接儿子,且low[v] >= dfn[u],则它的父亲u是割点
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e4+10;
const int maxm=1e5+10;
int n,m;
struct edge{
int v,next;
}e[maxm<<1];
int head[maxn],cnt=0;
int ok[maxn];
int dfn[maxn],low[maxn],tot=0;
void add(int u,int v){
e[cnt].v=v;
e[cnt].next=head[u];
head[u]=cnt++;
}
void tarjan(int u,int f){
dfn[u]=low[u]=++tot;
int siz=0;//子树的个数
int have = 1;
for (int i=head[u];~i;i=e[i].next){
int v=e[i].v;
if(v == f && have) {have = 0;continue;}//无向图的话可以处理重边
if(!dfn[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(u==f) siz++;
//不是根节点且顶点v通过回边能回溯到的最早的(dfn最小的点)没有比dfn[u]更小,u则是割点
//如果是根节点且子树大小大于等于2,则删去该点后子树之间不能连接
if((siz>=2 && u==f) || (low[v]>=dfn[u] && u!=f)) ok[u]=1;
}
else low[u]=min(low[u],dfn[v]);
}
}
int main(){
memset(head,-1,sizeof(head));
cnt=0;
cin>>n>>m;
for (int i=0;i<=n;i++){
dfn[i]=low[i]=ok[i]=0;
}
for (int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
for (int i=1;i<=n;i++){
if(!dfn[i]) {
tarjan(i,i);
}
}
int ans=0;
for(int i=1;i<=n;i++){
if(ok[i]) ans++;
}
cout<<ans<<endl;
for (int i=1;i<=n;i++){
if(ok[i]) printf("%d ",i);
}
return 0;
}
桥
点击查看代码块
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int maxn = 1e5+10;
vector<int> G[maxn];
int n;
int tot = 0;//tot记录时间戳
int low[maxn],dfn[maxn];
vector<pii> Ans;//存储桥的两点
int bridges = 0;//桥的数量
void init(){
Ans.clear();
bridges = 0;
tot = 0;
for (int i=0;i<n;i++){
G[i].clear();
low[i] = dfn[i] = 0;
}
}
void tarjan(int u,int f){
dfn[u] = low[u] = ++tot;
int have = 1;
for (int i=0;i<(int)G[u].size();i++){
int v = G[u][i];
if(v == f && have){//可以处理重边
have = 0;continue;
}
if(!dfn[v]){
tarjan(v,u);
low[u] = min(low[u],low[v]);
if(low[v] > dfn[u]) {//是桥
bridges++;
Ans.push_back(pii(min(u,v),max(u,v)));
}
}
else low[u] = min(low[u],dfn[v]);
}
}
int main(){
// freopen("1.out","w",stdout);
while(~scanf("%d",&n)){
init();
for (int i=0;i<n;i++){
int u;char c;
scanf("%d%c",&u,&c);
int num;
scanf("(%d)",&num);
for (int j=0;j<num;j++){
int v;
scanf("%d",&v);
G[u].push_back(v);
}
}
for (int i=0;i<n;i++){
if(!dfn[i]){
tarjan(i,i);
}
}
sort(Ans.begin(),Ans.end());
printf("%d critical links
",bridges);
for (auto i : Ans){
int u = i.first,v = i.second;
printf("%d - %d
",u,v);
}
puts("");
}
return 0;
}
/*
8
0 (1) 1
1 (3) 2 0 3
2 (2) 1 3
3 (3) 1 2 4
4 (1) 3
7 (1) 6
6 (1) 7
5 (0)
0
3 critical links
0 - 1
3 - 4
6 - 7
0 critical links
*/