有向图求最小环
Posted Harris-H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有向图求最小环相关的知识,希望对你有一定的参考价值。
文章目录
有向图求最小环
习题P2661
方法1floyd
三重循环扫一遍,然后
rep(i,1,n)
if(d[i][i]==inf) continue;
ans=min(ans,d[i][i]);
时间复杂度: O ( n 3 ) O(n^3) O(n3)
方法2dijkstra
对每个结点 d i j k s t r a dijkstra dijkstra一次。
思想与 f l o y d floyd floyd类似。
时间复杂度: O ( n ( n + m ) l o g n ) O(n(n+m)logn) O(n(n+m)logn)
方法3tarjan
找强连通分量的大小,对所有 > 1 >1 >1的取 m i n min min即可。
// Problem: P2661 [NOIP2015 提高组] 信息传递
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2661
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-11-19 13:11:49
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=2e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
const int hashmod[4] = 402653189,805306457,1610612741,998244353;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define x first
#define y second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define ios ios::sync_with_stdio(false),cin.tie(nullptr)
void Print(int *a,int n)
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\\n",a[n]);
template <typename T> //x=max(x,y) x=min(x,y)
void cmx(T &x,T y)
if(x<y) x=y;
template <typename T>
void cmn(T &x,T y)
if(x>y) x=y;
int n;
struct edge
int to,nt;
e[N<<1];
int s[N],tp,cnt,h[N];
void add(int u,int v)
e[++cnt]=v,h[u],h[u]=cnt;
int vis[N],dfn[N],low[N],id;
int ans;
void dfs(int u,int fa)
dfn[u]=low[u]=++id;vis[u]=1;s[++tp]=u;
for(int i=h[u];i;i=e[i].nt)
int v=e[i].to;
if(!dfn[v])
dfs(v,u);
low[u]=min(low[u],low[v]);
else if(v!=fa)
low[u]=min(low[u],dfn[v]);
if(dfn[u]==low[u])
int sum=1;
while(s[tp]!=u)
vis[s[tp--]]=0,sum++;
tp--;
if(sum==2)
puts("2");exit(0);
if(sum>1) ans=min(ans,sum);
int main()
scanf("%d",&n);
rep(i,1,n)
int x;scanf("%d",&x);
add(i,x);
ans=inf;
rep(i,1,n) if(!dfn[i]) dfs(i,0);
printf("%d\\n",ans);
return 0;
方法4 拓扑排序
从所有入度为0的点开始 b f s bfs bfs,然后标记,显然这些点不可能组成环,然后再对没有标记的点进行暴力 d f s dfs dfs找环,不断取 m i n min min即可。
方法5 并查集求环
#include<cstdio>
#include<iostream>
using namespace std;
int f[200002],d[200002],n,minn,last; //f保存祖先节点,d保存到其祖先节点的路径长。
int fa(int x)
if (f[x]!=x) //查找时沿途更新祖先节点和路径长。
int last=f[x]; //记录父节点(会在递归中被更新)。
f[x]=fa(f[x]); //更新祖先节点。
d[x]+=d[last]; //更新路径长(原来连在父节点上)。
return f[x];
void check(int a,int b)
int x=fa(a),y=fa(b); //查找祖先节点。
if (x!=y) f[x]=y; d[a]=d[b]+1; //若不相连,则连接两点,更新父节点和路径长。
else minn=min(minn,d[a]+d[b]+1); //若已连接,则更新最小环长度。
return;
int main()
int i,t;
scanf("%d",&n);
for (i=1;i<=n;i++) f[i]=i; //祖先节点初始化为自己,路径长为0。
minn=0x7777777;
for (i=1;i<=n;i++)
scanf("%d",&t);
check(i,t); //检查当前两点是否已有边相连接。
printf("%d",minn);
return 0;
有向图最大环
同理也可以用上述方法求有向图最大环。
P5145
其实 n n n点 n n n条出边的有向图就是基环树森林。
可以用 d f s dfs dfs解决。
#include <bits/stdc++.h>
using namespace std;
#define reg register
const int N = 100000 + 5;
int n, d[N], t[N], a[N], b[N], ans, st;
void dfs(int x, int sum)
if (x==st) ans=max(ans, sum); return;
if (a[x] || b[x])return;
a[x]=1; dfs(d[x], sum+t[x]); a[x]=0;
int main()
scanf("%d", &n);
for (reg int i=1; i<=n; ++i) scanf("%d%d", &d[i], &t[i]);
for (st=1; st<=n; ++st) dfs(d[st], t[st]), b[st]=1;
printf("%d\\n", ans);
return 0;
用tarjan也很好写,把边权赋给源点即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 1000100
struct Edge
int v, nx;
e[MAXN];
inline int min(int a, int b)
if (a < b) return a;
else return b;
inline int max(int a, int b)
if (a > b) return a;
else return b;
int head[MAXN], tim, st[MAXN], top, ecnt, n, m, x, y, dfn[MAXN], low[MAXN], in[MAXN], si[MAXN], num, se[MAXN], ans;
bool vis[MAXN];
void add(int f, int t)
e[++ecnt] = (Edge) t, head[f];
head[f] = ecnt;
void tarjan(int u)
dfn[u] = low[u] = ++tim;
st[++top] = u;
vis[u] = 1;
for (int i = head[u]; i; i = e[i].nx)
int v = e[i].v;
if (!dfn[v])
tarjan(v);
low[u] = min(low[v], low[u]);
else if (vis[v]) low[u] = min(low[u], dfn[v]);
if (low[u] == dfn[u])
int v;
num++;
do
v = st[top--];
in[v] = num;
vis[v] = 0;
si[num] += se[v];
while (u != v);
int main()
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d%d", &x, &y);
add(i, x);
se[i] = y;
for (int i = 1; i <= n; i++)
if (!dfn[i]) tarjan(i);
for (int i = 1; i <= num; i++)
ans = max(ans, si[i]);
printf("%d\\n", ans);
return 0;
也可以用拓扑排序后,暴力找环即可。
#include<bits/stdc++.h>
using namespace std;
int n,ans,idx;
int head[100010],ru[100010];
struct node
int nxt,to,w;
edge[100010];
void add(int u,int v,int wPOJ 1734 无向图最小环/有向图最小环
ACM图论—最小环问题 ( 仔细分析+理解+代码 )(HDU 1599 ) (POJ 1743)