防坑大招:先上一张母上大人设计的有向有环图,作为测试数据,(见附图)
要求:找到所有能到达4的节点x,can[x]数组中置对应值为1
建边时,通过x!=y, 将自环边直接滤掉
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
if(x!=y) {v[x].push_back(y);p[y].push_back(x);}
}
从终点t出发能到达的节点,此时t相当于根,等同于:以t为出发点,判断有向图的连通性。
反向建图,采用有向图的DFS遍历,所能达到的点构成一颗生成树。
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
if(x!=y) {v[x].push_back(y);p[y].push_back(x);} //y-x,建反图
}
scanf("%d%d",&s,&t);can[t]=1;
find(t); //从终点开始遍历
void find(int x)
{
vis[x]=1;can[x]=1; //先置can标志,代表从该节点可以到达终点
for(int i=0;i
正向DFS遍历判断连通性,代表从起点所能达到节点,需要遍历结束才能确认是否能达到终点t,再由t回溯时向前置can标志。
在DFS结束后置can标志,如果某点的孩子can,则它的父亲也can.
但是,can数组不对!遍历时每个节点按DFS序走,不能重复走!
成环的节点必然会重复走。比如:图中的2-3-2-4, 3节点可到达4,但必须二次经过2才可以!
for(int i=1;i<=n;i++) if(!vis[i]) find(i); //main函数
void find(int x)
{
vis[x]=1;
if(x==t) return;
for(int i=0;i
图中2-3-2, 3-13-3,2-5-6-2、6-7-8-7等,节点2、3、6等重复走。
引入out数组,计算每个节点的出度,只有还有边可走,就继续,不管该节点有没有访问过。
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
if(x!=y) {v[x].push_back(y);out[x]++;p[y].push_back(x);} //读边时计算出度out
}
for(int i=1;i<=n;i++) if(!vis[i]) find(i);
void find(int x)
{
vis[x]=1;
if(x==t) return;
for(int i=0;i0||!vis[v[x][i]]) {out[x]--;find(v[x][i]);} //加入out判断,--
if(can[v[x][i]]) can[x]=1;
}
}
此种写法相当于欧拉通路的判断,每个边走一次
然后栈中的点都是可以到达的,弹栈置can数据值
然而,还不如直接用欧拉通路的算法更简洁。。。
void dfs(int i)
{
for (int j=0; j
1-9-2-10-9-2-4, 9-2边重复走。其实,也相当于有割边唉~~~
边允许重复走,top数据不起作用。尝试清除vis[]标志,允许重复走。结果可以,没有WA,但TLE!
多次扫描,效率太低~~
void find(int x)
{
if(x==t) return;
if (can[v[x][i]]==1) return; //剪枝
for(int i=0;i
visit[x]=0,1,2... 其中:>=2代表多次访问,成环;
此方法一般用于判断是否有环,简单环
然而。。。只有40分
void find(int x)
{
vis[x]++;
for(int i=0;i
先缩点,按有向无环图遍历。
对于同一个连通分量中的点,只要有一个点到达,则连通分量中所有点全可以到达。
void tarjan(int x)
{
sa.push(x);ins[x]=1;
dfn[x]=low[x]=++indexs;
for(int i=0;i
交上去AC掉了,还行,跑的挺快。除了代码量较大别的都很完美。
有向带环图正向DFS的终极大法!!!