图的连通

关于判断图是否连通,一般需要先了解:
1. DFSBFS这两个搜索算法,或者说是遍历方式(实际上树的前中后序遍历和层次遍历分别就是DFS和BFS的运用)。
2. 对于图的建立此处就不在介绍了
(这里不对UnionFind在连通性的判断上做介绍。)


连通图的概念

这个链接里面,都有讲到,这个
可能大神讲到有点抽象,小弟就做了如下图解(如有误,喷我)。
割点集与割边集
割点与桥
此外,链接中所讲的双连通分量,可以理解为连通度大于等于2的情况,也就是 u v之间至少存在两条不同 的路径可达。
其中的dfs[],和low[]这两个数组可以,结合这个动图理解,这个动图

万能模板——Tarjan

之所以我认为这个Tarjan是一个解决连通性问题的利器(厉害还是Tarjan厉害)。

/*
 *邻接表存储
 *无向图求割点
*/
vector<int> G[MAXN];
int dfn[MAXN], low[MAXN], keyNode[MAXN];
bool vis[MAXN];
void Tarjan(int u, int start, int &cnt, int &res, int &rd) {
    cnt++;
    dfn[u] = low[u] = cnt;
    for(int i = 0; i < (int)G[u].size(); ++i) {
        int v= G[u][i];
        if(!vis[v]) {
            Tarjan(v, start, cnt, res, rd);
            if(low[v] >= dfn[u]) {
                if(u != start && !vis[u])
                    keyNode[res] = u, vis[u] = true, res++;
                else if(u == start)
                    rd++;
            }
            if(low[v] < low[u]) low[u] = low[v];
        } else if(dfn[v] < low[u]) {
            low[u] = dfn[v];
        }
    }
}
int keyVertex(int n) {
    int cnt = 0, res = 0;
    for(int i = 0; i <= n; ++i)
        dfn[i] = 0, vis[i] = false;
    for(int i = 0; i < n; ++i) {
        if(!dfn[i]) {
            int rd = 0;
            Tarjan(i, i, cnt, res, rd);
            if(rd > 1 && !vis[i])
                keyNode[res] = i, vis[i] = true, res++;
        }
    }
    return res;
}
/*
 *邻接表存储
 *无向图求割边
*/
struct KeySide{
    int from, to;
}keySide[MAXE];

vector<int> G[MAXN];
int dfn[MAXN], low[MAXN];

void Tarjan(int u, int &cnt, int &res) {
    ++cnt;
    dfn[u] = low[u] = cnt;
    for(int i = 0; i < (int)G[u].size(); ++i) {
        int v = G[u][i];
        if(!dfn[v]) {
            Tarjan(v, cnt, res);
            if(low[v] > dfn[u])
                keySide[res].from = u, keySide[res].to = v, res++;
            if(low[v] < low[u])
                low[u] = low[v];
        } else if(dfn[v] < dfn[u] - 1 && dfn[v] < low[u]) {
            low[u] = dfn[v];
        }
    } 
} 
int keyEdge(int n) {
    int cnt = 0, res = 0;
    for(int i = 0; i <= n; ++i) dfn[i] = 0;

    for(int i = 0; i < n; ++i)
        if(!dfn[i])
            Tarjan(i, cnt, res);
    return res;
}
/*
 *无向图求连通分支
*/
vector<int> G[MAXN];
int dfn[MAXN], low[MAXN];
stack<int> S;

void Tarjan(int u, int &cnt, int &res)
{
    low[u] = dfn[u] = ++cnt;
    S.push(u);
    for(int i = 0; i < (int)G[u].size(); ++i) {
        int v = G[u][i];
        if(!dfn[v]) {
            Tarjan(v, cnt, res);
            if(low[u] > low[v]) low[u] = low[v];
        } else if(low[u] > dfn[v]){
            low[u] = dfn[v];
        }
    }
    if(low[u] == dfn[u]) {
        int v; res++;
        do{
            v = S.top(), S.pop();
            cout<<v<<' ';
        }while(u != v);
        cout<<endl;
    }
}
void block(int n) {
    int cnt = 0, res = 0;
    for(int i = 0; i <= n; ++i)
        dfn[i] = 0;
    for(int i = 0; i < n; ++i) {
        if(!dfn[i]) {
            Tarjan(i, cnt, res);
        }
    }
}
/*
 *有向图求连通分支
*/
vector<int> G[MAXN];
int low[MAXN], dfn[MAXN], id[MAXN];
bool inSta[MAXN];
stack<int> S;

void Tarjan(int u, int &cnt, int &res) {
    low[u] = dfn[u] = ++cnt;
    S.push(u), inSta[u] = true;
    for(int i = 0; i < (int)G[u].size(); ++i) {
        int v = G[u][i];
        if(!dfn[v]) {
            Tarjan(v, cnt, res);
            if(low[u] > low[v]) low[u] = low[v];
        } else if(inSta[v]) {
            if(low[u] > dfn[v]) low[u] = dfn[v];
        }
    }
    //缩点
    if(low[u] == dfn[u]) {
        int v; res++;
        do{
            v = S.top(), S.pop();
            inSta[v] = false;
            id[v] = res;
        }while(u != v);
    }
}
int slove(int n) {
    int cnt = 0, res = 0;
    for(int i = 0; i <= n; ++i)
        low[i] = dfn[i] = id[i] = 0, inSta[i] = false;
    for(int i = 1; i <= n; ++i)
        if(!dfn[i]) Tarjan(i, cnt, res);
    return res;
}

就写到这吧,发现自己已经对于有向和无向的求解已经傻傻分不清了,过段时间做几道题,在看看应该能想明白。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二值连通分量提取可以通过以下步骤实现: 1. 定义一个函数模块,用于提取连通分量。该函数会改变传入的像,因此建议在传参前拷贝像或传入copy像。该函数返回两个参数,一个是各个连通分量包含的像素点数,一个是对应连通分量的像。 2. 在循环中,判断像中是否存在前景目标。如果不存在,则说明连通分量提取完毕。 3. 获取前景像素的横坐标,并用上一步迭代的结果判断当前连通分量是否与上一次相同,以确定迭代过程是否结束。 4. 对当前连通分量进行膨胀,并与原求交,判断是否满足内部迭代结束条件。如果满足,则将提取的连通分量保存到img_pixels中,并将src***任意的初始值赋值给canvas,提取下一次的连通分量。 以下是Python代码示例: ```python import numpy as np import cv2 def extract_connected_components(img): num_pixels = [] img_pixels = [] canvas = np.zeros_like(img) while img.any(): x, y = np.where(img > 0) xk = np.column_stack((x, y)) if len(xk) == 0: break if len(num_pixels) > 0 and len(xk) == len(num_pixels[-1]): break canvas.fill(0) for i in range(len(xk)): cv2.circle(canvas, (y[i], x[i]), 1, 255, -1) while True: tmp = canvas.copy() canvas = cv2.dilate(canvas, np.ones((3, 3), np.uint8), iterations=1) canvas = cv2.bitwise_and(canvas, img) if (tmp == canvas).all(): break if (tmp == canvas).all(): img_pixels.append(canvas) num_pixels.append(len(xk)) img = cv2.bitwise_xor(img, canvas) else: break return num_pixels, img_pixels ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值