有向图求最小环

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)

LeetCode 210. Course Schedule II(拓扑排序-求有向图中是否存在环)

有向图中的最小环问题

bzoj1486 HNOI2009—最小圈

codeforces Gym 101572 I 有向图最小环路径