这是一道很好的练习强联通的题目。 首先,从题中可以看到,题目的要求就是要我们求出从起点到终点是否可以经过flag = 1 的边。 由于是无向图,且要求很多,直接暴力dfs会很凌乱。
那么,我们就想到一个思路:能不能尽量把这张图缩小,标记转为点,最好成为一条一条链呢?
tarjan的缩点!!
没错,对于一个环,可以想到,只要这个环中有一条边flag = 1,那么所有的点我们都可以通过falg = 1的边到达(因为这是环)。所以,不妨进行tarjan缩点,只要这个缩点中有一条边falg = 1,我们就把这个缩点打上tag。
再一想,经过缩点之后,原来十分凌乱的图就变成了一棵树。到达终点的路线也就只有固定一条了。这里我选择dfs。
思路大体就是这样,总时间复杂度O(M + N)
话不多说,具体细节操作标记在代码里面了。
#include<bits/stdc++.h> using namespace std; #define N 500010 #define isdigit(c) ((c)>='0'&&(c)<='9') inline int read(){ int x = 0,s = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); } while(isdigit(c)){ x = (x << 1) + (x << 3) + (c ^ '0'); c = getchar(); } return x * s; } int n, m; struct node{ int u, v, flag; int next; } t[N << 1]; int f[N];//日常邻接表 int s, ht;//起点, 终点 int dfn[N], low[N], scc[N]; //scc即为缩点后每个缩点的编号 stack <int> stac;//缩点用的 bool vis[N];//一个点是否被经过 int bian = 0; void add(int u, int v, int flag){ bian++; t[bian].u = u; t[bian].v = v; t[bian].flag = flag; t[bian].next = f[u]; f[u] = bian; return ; } int cnt = 0, cac;//cac为强联通的数量 void tarjan(int now, int fa){//无向图的tarjan强联通 板子 dfn[now] = low[now] = ++cnt; vis[now] = 1; stac.push(now); for(int i = f[now]; i;i = t[i].next){ int v = t[i].v; if(v != fa){ if(!dfn[v]){ tarjan(v, now); low[now] = min(low[now], low[v]); } else if(vis[v])low[now] = min(low[now], low[v]); } } if(dfn[now] == low[now]){ int cur; cac++; do{ cur = stac.top(); stac.pop(); scc[cur] = cac; vis[cur] = 0; }while(cur != now); } return ; } bool tong[N]; //tong为每个缩点被打上的标记,即上文所说的,是否包含flag = 1的边 void dfs(int now, bool flag){ if(tong[now])flag = 1;//这个缩点标记为1的话,记下来 if(now == scc[ht]){ if(flag)puts("YES");//搜到终点,没什么好说的 else puts("NO"); return ; } for(int i = f[now]; i;i = t[i].next){ int v = t[i].v, u = t[i].u; if(!vis[v]){ vis[v] = 1; dfs(v, flag | t[i].flag);//这里要注意不要漏掉了缩点与缩点之间的边的 flag } } return ; } int main(){ n = read(), m = read(); for(int i = 1;i <= m; i++){ int x = read(), y = read(), tag = read(); add(x, y, tag);add(y, x, tag); } s = read() , ht = read(); tarjan(1, 0); for(int i = 1;i <= bian; i += 2){ if(scc[t[i].u] == scc[t[i].v] && t[i].flag){ tong[scc[t[i].u]] = 1;//为强联通分量中的边,且flag = 1 } } memset(f, 0, sizeof(f));//重复利用 bian = 0; memset(vis, 0, sizeof(vis)); for(int i = 1;i <= m << 1; i++){ int u = t[i].u, v = t[i].v; if(scc[u] != scc[v]){ add(scc[u], scc[v], t[i].flag);//不同缩点之间的连边,需要保留。flag不能改 } } vis[scc[s]] = 1; dfs(scc[s], 0);//dfs的都是缩点,这点不要忘了 return 0; }
来源:https://www.cnblogs.com/wondering-world/p/12634460.html