标签:概念 表示 top 大于 bre 要求 记录 导致 一个栈
vis数组:在图的遍历中,往往设置了一个标记数组vis的bool值来记录顶点是否被访问过。但有些时候需要改变vis值的意义。令vis具有3种值并表示3种不同含义
vis = 0,表示该顶点没没有被访问
vis = 1,表示该顶点已经被访问,但其子孙后代还没被访问完,也就没从该点返回
vis = 2,表示该顶点已经被访问,其子孙后代也已经访问完,也已经从该顶点返回
可以vis的3种值表示的是一种顺序关系和时间关系。
DFS过程中,对于一条边u->v
vis[v] = 0,说明v还没被访问,v是首次被发现,u->v是一条树边又称树枝边
vis[v] = 1,说明v已经被访问,但其子孙后代还没有被访问完(正在访问中),而u又指向v?说明u就是v的子孙后代,u->v是一条后向边,因此后向边又称返祖边
vis[v] = 2,说明v已经被访问,其子孙后代也已经全部访问完,u->v这条边可能是一条横叉边,或者前向边
联通:两点存在一条连接它们的路径。
联通块:里面的点两两联通。
桥:对于一个联通无向图,一条边是桥当且仅当去掉这条边后图变得不连通。
强连通分量:没有桥的联通块。
(无向图的强连通分量也叫边双联通分量,对应的概念是:
点双连通:没有割点的双连通分量
对于特定的题目会有求双连通分量的操作。)
? tarjan算法判桥:
? dfn[x]是dfs序,代表x是第几个搜到的。
? instack[x]表示当前的点是否在dfs栈中,是用来判断是否是返祖边,代码中if(instack[y])即代表x点在y的子树中。
? 不过无向图联通分量中instack数组是不必要的。
由于无向图中都是双向边,前向边的情况对不更新low(tarjan中用if(instack[y])将前向边特判掉不更新)没有意义(证明:如果存在一条前向边u->v,则亦存在v->u,所以在dfs到v时已经更新了v。再dfs到v时,即使不更新也没意义了)所以
? low[x] PPT:x通过非返祖边,且至多通过一条非树边能到达的最小dfn。
? 我的理解:x至多通过一条非树边(即不是返回父亲的返祖边)能到达的最小dfn。
? 感觉洪老师把返祖边的定义弄错(混)了:一方面字面上以为是返回父亲的边,另一方面代码里用的是instack正确的判断,导致没有判真正返回父亲的边。
void tarjan(int x,int fa) {
instack[x] = 1;
dfn[x] = low[x] = ++tot;
for (auto y : E[x]) {
if (!dfn[y]) {
tarjan(y,x);
low[x] = min(low[x], low[y]);
}
else {
if(y!=fa&&instack[y])low[x] = min(low[x], dfn[y]);
}
}
instack[x] = 0;
}
//-----ppt中的错误代码,没有特判掉指向父亲的返祖边--------------
void tarjan(int x) {
instack[x] = 1;
dfn[x] = low[x] = ++tot;
for (auto y : E[x]) {
if (!dfn[y]) {//树边
tarjan(y);
low[x] = min(low[x], low[y]);
}
else {
if(instack[y])low[x] = min(low[x], dfn[y]);//返祖边,一般指向父亲
}
}
instack[x] = 0;
}
判桥: low[x]<dfn[x]代表存在一条返回到父亲之上某点y的返祖边,y有一条路径到x,x有另一条路径到y。(形成双连通分量??)
反之low[x]>=dfn[x]代表它连向父亲的边为桥
强连通分量:任意两点都可以互相到达。
在做 Tarjan算法时,如果 tarjan(x) 后发现 dfn[x]==low[x],则 x 的?树?的剩下的所有点构成?个强连通分量,可以用一个栈来维护:
stack<int> S;
void tarjan(int x) {
S.push(x);
instack[x] = 1;
dfn[x] = low[x] = ++tot;
for (auto y : E[x]) {
if (!dfn[y]) {
tarjan(y, x);
low[x] = min(low[x], low[y]);
}
else {
if ( instack[y])low[x] = min(low[x], dfn[y]);
}
}
if (low[x] == dfn[x]) {
while (1) {
int now = S.top();
S.pop();
instack[x] = 0;//把之前没有清零的一次性更新。
/*
染色,统计之类的操作
*/
if (now == x)break;
}
}
}
题意:
有N个?,给你M对整数 (a,b),表示第 a 个?认为 b 很厉 害,?这种关系具备传递性,也就是如果 a 认为 b 厉害, 且 b 认为 c 厉害,则 a 认为 c 厉害
求有多少?被所有?都觉得很厉害
N,M<=10^5
做法:
无环:有向图无环 -> 至少有一个点没有出边。于是任意找一个点,一直走到没有出边的点,判一下是否所有点都连向它。
有环:对于这样一个环,必为强连通,缩点后变成无环情况(会多出重边)。
套路:一个题在没有环(是一个拓扑图)的时候很好做,就考虑缩环。
题意:
给定一个有向图,N个点,求:
1)至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点
2)至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点
思路:
按照套路,Tarjan算法求SCC,并缩点建图。那么对于问题1,新的图中入度为0点的点数即是答案。
对于问题2,答案为max(入度为0的点的点数,出度为0的点数),因为对于每个入度或出度为0的点,需要连一条边来解决,那么将出度为0的点连向入度为0的点是最优的。
此外,如果最后SCC只有1个,那么问题2的答案应该特判为0。
套路:有向图构造联通分量的方法:将出度为0的点连到入度为0的点上有向图。
? 无向图构造联通分量的方法:将度为1的点(叶子)相互连接。
题意:有n个牧场,Bessie 要从一个牧场到另一个牧场,要求至少要有2条独立的路可以走。现已有m条路,求至少要新建多少条路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指:没有公共边的路,但可以经过同一个中间顶点。给出的图保证已经联通。
思路:
在同一个边双连通分量(即无向边的强连通分量)中,任意两点都有至少两条独立路可达,所以同一个边双连通分量里的所有点可以看做同一个点。
套路缩点后,新图是一棵树,因为原图已联通。现在就是要在树上添边,令所有点缩为一个边双联通分量。
再套路连叶子,答案就是(树上度为1的点数+1)/2。
题意:有一个公园有n个景点,公园的管理员准备修建m条道路,并且安排一些形成回路的参观路线。如果一条道路被多条道路公用,那么这条路是冲突的;如果一条道路没在任何一个回路内,那么这条路是不冲突的。问分别有多少条有冲突的路和没有冲突的路
思路:
首先非多余的边很明显是桥,因为其不在任何一个回路中。
那么冲突边是多个回路共用的边,注意此处的回路是简单回路,那么考虑点BCC缩点建图,如果一个点BCC里的边数大于点数,那么其至少2个环,其中的所有边的都是冲突边。如果点数等于边数,那么只有一个大环,没有冲突变,而点BCC中不存在点数少于边数的情况。
标签:概念 表示 top 大于 bre 要求 记录 导致 一个栈
原文地址:https://www.cnblogs.com/SuuT/p/11247343.html