专栏文章

一些有关图论的模板。

算法·理论参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min20q6c
此快照首次捕获于
2025/12/01 19:16
3 个月前
此快照最后确认于
2025/12/01 19:16
3 个月前
查看原文

最短路

dijkstra

CPP
memset(d,0x3f,sizeof(d));
priority_queue< pair<int,int> > q;
q.push(make_pair(0,s));
d[s]=0; 
while(!q.empty){
	int u=q.top().second;
    q.pop();
    if(f[u]) continue;
    f[u]=1;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(d[v]>d[u]+e[i].w){
			d[v]=d[u]+e[i].w;
			if(!f[v]) q.push(make_pair(-d[v],v));
		}
	}
}

SPFA

CPP
memset(d,0x3f,sizeof(d));
d[s]=0,f[s]=1;
queue<int> q;
q.push(s);
while(!q.empty()){
	int u=q.front();
	q.pop(),f[u]=0;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(d[v]>d[u]+e[i].w){
			d[v]=d[u]+e[i].w;
			if(!f[v]){
				q.push(v);
				f[v]=1;
			}
		}
	}
}

次短路

  1. 求最短路过程中顺带更新次短路。(可重复走边须 SPFA )
  2. 起点、终点各跑一次最短路,枚举各点。次短路即为某一条边长度 + 起点到一个端点最短 + 终点到另一端点最短。

有关连通性

Tarjan强连通分量

sccscc 记录强连通分量个数,dfndfn 时间戳,lowlow 返祖边可追溯最早时间戳。
ff 是否在栈中,colcol SCC 染色。
CPP
int cnt,scc,dfn[],low[],f[],col[];
stack<int> s;
void Tarjan(int u){
	dfn[u]=low[u]=++cnt;
	s.push(u),f[u]=1;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(!dfn[v]){
			Tarjan(v);
			low[u]=min(low[u],low[v]);
		}else if(f[v]){
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(low[u]==dfn[u]){
		scc++;
		int x=-1;
		while(x!=u){
			x=s.top();
			s.pop();
			f[x]=0;
			col[x]=u;
		}
	}
}

割点

  1. uu 为树根,且子树数量 >1>1
  2. uu 不为树根,存在树枝边 (u,v)(u,v) 满足 dfnulowvdfn_u\leq low_v
tmptmp 统计 dfs 树中 uu 的树枝边数,cc 储存割点,c0c_0 为割点数量。
CPP
int cnt,dfn[],low[],root,c[]; 
void Tarjan(int u,int fr){
	dfn[u]=low[u]=++cnt;
	int tmp=0;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(!dfn[v]){
			tmp++;
			Tarjan(v);
			low[u]=min(low[u],low[v]);
			if((u==root && tmp>1) || (u!=root && dfn[u]<=low[v])){
				if(!c[u]) c[0]++;
				c[u]=1;
			}
		}else if(v!=fr){
			low[u]=min(low[u],dfn[v]);
		}
	}
}

点双连通分量

  1. Tarjan 找割点并把 dfs 遍历点入栈。
    找到割点就弹栈直到弹出割点。弹出点即为该点双连通分量节点。割点重新入栈。
    dfs 结束时将栈弹空。所弹出点也为一个点双连通分量。
  2. 省略根节点判断,将割点判断条件直接改为 dfnulowvdfn_u\leq low_v
    同上进行入栈弹栈(发现孤立点或符合条件点)。

割边(桥)

存在树枝边,满足 dfnu<lowvdfn_u<low_v
CPP
int cnt,dfn[],low[];
vector<int> b[];
void Tarjan(int u,int fr){
	dfn[u]=low[u]=++cnt;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(!dfn[v]){
			tmp++;
			Tarjan(v);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<low[v]){
				if(u<v) b[u].push_back(v);
				else b[v].push_back(u);
			} 
		}else if(v!=fr){
			low[u]=min(low[u],dfn[v]);
		}
	}
}

边双连通分量

  1. Tarjan 找桥,dfs 点入栈。
    找到桥就弹栈,直至栈顶为 dfs 树父节点。dfs 结束后将栈弹空。
  2. 将无向图看作有向图,禁止走同一无向边拆分出的回边。
    寻找该有向图 scc。scc 即为边双连通分量。

评论

0 条评论,欢迎与作者交流。

正在加载评论...