双连通分量小结

博客介绍了图论中的割点、割边、点双连通分量和边双连通分量的概念,分析了两者区别与联系。还阐述了二分图的判定方法及相关定理,如双连通分量含奇圈与二分图的关系。最后说明了边双连通分量的查找方法及双连通分量建边和遍历的注意点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

割点(割顶):割点即割顶,去掉割点后,原图的连通分量变多;
割边(桥):割边即桥,去掉割边后,原图的连通分量变多;

点双连通分量:任意两点之间存在至少两条顶点不同的路径/去掉任意一个点后,图的连通性不变
边双连通通分量:任意两点之间存在至少两条边不同的路径/去掉任意一条边后,图的连通性不变

点双连通和边双连通的区别与联系:

  1. 两者都是基于无向图而言的
  2. 点双连通分量一定是边双连通分量(除了两点一线,两点一线是点双,但不是边双),反之不一定
  3. 点双连通分量之间可以有公共点,但也只能有一个(该点是割点);因此在求点双连通的时候,可以找割点,但是入栈的要是边,因为一个点可能属于多个点双连通分量,若存的是点,那割点出栈后还要入栈。如果存的是边,则不用,但出边的时候要判断边的两个端点是否和之间出去的有重复
  4. 边双连通分量之间不能有公共点或公共边,连接两个边双连通分量的是割边;因此边双连通就方便了,一个点只能属于一个边双连通分量

二分图:下面针对的是无向图
判定:先对任意一个顶点染色,然后找它相邻点
    *若为染色,则染色(递归此点)
    *已染色,且颜色和当前结点相同(失败,该图不是二分图)
    *已染色,且颜色和当前结点不同,跳过
    
一些定理:
    *如果一个双连通分量内的某些顶点在一个奇圈中(即双连通分量含有奇圈),那么这个双连通分量的其他顶点也在某个奇圈中
    证明:假设有一个奇圈,因为是点双,没有割点,必然有紧挨着的圈,设公共点有k个(至少有两个公共点),假设这个圈是偶圈,则这个偶圈必然能和原来的奇圈组成新的奇圈(新的圈=(奇圈-k)+(偶圈-k)= 奇数);这里k是两个圈的公共边上的点数(不包含端点);如果这个圈是奇圈,则也得证
    *如果一个双连通分量含有奇圈,则它一定不是二分图;反之,如果一个图是二分图,它一定没有奇圈

   对于定理一,如下图,若有一个奇圈(1,2,3,4,5),和它相邻的有一个偶圈(4,5,3,6);则它们的k是1(即公共边<4,5> <5,3>这两条边上的点5,端点3,4不算);新的圈=(5-1)+(4-1)-2 =7,即(1,2,3,6,4).组成一个新的奇圈;即若一个双连通分量内含有一个奇圈,则其他点必定在某个奇圈中(一个点可能属于多个奇圈)

//代码:
/**
求解方法:不断把某个点的树边和方向边入栈,更新当前点的Low,如果某个点是割点,就把栈里存的边弹出
注意:子节点的low可以用来更新父节点的low,而方向边只能通过dfn来更新当前点的low。不能拿方向边的点(祖先结点)的Low来更新当前low,因为祖先结点的low不一定根当前结点有关系
*/
void tarjan(int u,int fa)
{
    int child = 0;
    low[u] = dfn[u] = ++dfn_cnt;
    for(int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        pii p = MP(u,v);	//取得当前这条边
        if(!dfn[v])			//如果还没被访问,说明是一条树边
        {
            S.push(p);	//必须在递归前推入栈中
            child++;
            tarjan(v,u);
            low[u] = min(low[u],low[v]);
            if(low[v] >= dfn[u]) ///***当前子节点会不会连向u的祖先结点,如果不会,则u去掉后,v子分支
                				//肯定就断开了。这个判断要在tarjan完这个子节点就判断,因为一个
                				//结点u可以连着多个点双,不会相互影响,相反,如果等遍历完所有子节点
                				//才判断就不行了。这里不能换成low[v] >= low[u],应该是和u的dfs序
                				//比较,看是不是会连到u的祖先结点上
            {
                iscut[u] = true;
                bcc_cnt++;
                bcc[bcc_cnt].clear();
                for(;;){
                    pii tmp = S.top(); S.pop();
                    //判重
                    if(bccno[tmp.X] != bcc_cnt) bcc[bcc_cnt].PB(tmp.X),bccno[tmp.X] = bcc_cnt;
                    if(bccno[tmp.Y] != bcc_cnt) bcc[bcc_cnt].PB(tmp.Y),bccno[tmp.Y] = bcc_cnt;
                    if(tmp.X == u && tmp.Y == v) break;
                }
            }
        } else if(dfn[v] < dfn[u] && v != fa)//该点已被访问过,看看是不是在u的上边,若是,则是一条
            								//回边,入栈
            								//这里v!=fa也很重要,因为双连通是针对无向图,我们建图的时候是有正反向边的。
        {
            S.push(p);
            low[u] = min(low[u],dfn[v]);//这里不能用min(low[u],low[v]),v是u的祖先结点,祖先结点
            						//的low值对其孩子结点来说是不适用的
        }
    }
    if(fa < 0 && child > 1) iscut[u] = true;
}

对于下述情况,经过dfn[a1] < dfn[u],注意这里比较的是结点的先后次序,不是Low值;主要是看该结点是不是u的回向边,所以是根据dfn来比较的;然后low[u] = min(low[u],dfn[v]);这里不能用low[v],不然u的箭头就指向a0了,这样整条线构成点双,但实际情况并不是

 

边双连通分量

    对于边双连通,第一遍dfs找出所有的桥,第二遍dfs遍历整个图,遇到桥或已经遍历过的结点则停止。即可找出每个边双连通分量。此种做法可以判重边和环,如果1--2(1,2连边)则有两个边双,如果是1--2,2--1则有一个边双。具体过程可以参考代码模拟一遍即可。

void tarjan(int u,int fa)
{
    low[u] = dfn[u] = ++dfn_cnt;
    for(int i = head[u]; ~i; i = edge[i].next)
    {
       int v = edge[i].v;
       //dbg(u,v,"*");
       if(!dfn[v])
       {
           tarjan(v,u);
           low[u] = min(low[u],low[v]);
           if(low[v] > dfn[u])
            {
                isbridge[i] = isbridge[i^1] = true;
                //dbg("****");
            }
       }else if(dfn[v] < dfn[u] && v != fa)
            low[u] = min(low[u],dfn[v]);
    }
}

void dfs_bcc(int u,int id)
{
    bccno[u] = id;
    for(int i = head[u]; ~i; i = edge[i].next)
    {
        if(!isbridge[i])
        {
            int v = edge[i].v;
            if(!bccno[v]) dfs_bcc(v,id);
        }
    }
}

注意点:

   双连通(包括边和点)分量都是针对无向图,因此建边的时候要建立正反边。并且dfs的时候记得判断某点的子边是不是它的父节点。任意两个边双连通分量之间都没有公共边,公共点(因此可以缩点??),这点和强连通分量一样。缩点建图后的图一定没有环

### 回答1: 加权图连通划分问题是指将一个加权无向图分成若干个连通块,使得每个连通块的权值之和最小。这个问题可以建立混合整数线性规划模型来求解。 假设有一个无向图G=(V,E),其中V={1,2,…,n}表示节集合,E表示边集合。每条边e=(i,j)都有一个权值w(e)。假设图G有k个连通块,我们用一个01变量$x_{ij}$表示节i和节j是否在同一个连通块中。用一个整数变量$y_i$表示节i所在的连通块编号。则可以建立如下的混合整数线性规划模型: 目标函数:minimize $\sum_{e\in E} w(e) \cdot x_{ij}$ 约束条件: 1. 每个节i都必须属于一个连通块:$\sum_{j\in V} x_{ij} = 1, \forall i\in V$ 2. 连通块的数量必须等于k:$\sum_{i\in V} [y_i=j] = k, \forall j=1,2,...,k$ 3. 连通块的编号必须是连续的自然数:$y_1 \leq y_2 \leq ... \leq y_n$ 4. 连通块内的节必须相互连通:$x_{ij} \leq \sum_{l\in S} x_{il}, \forall i\in V, \forall j\in V-S, S\subseteq V, i\in S, j\notin S$ 5. $x_{ij}$和$y_i$都是0或1:$x_{ij},y_i \in \{0,1\}, \forall i\in V, \forall j\in V$ 其中,约束条件4是连通性约束,它表示如果节i和节j在同一个连通块中,那么它们之间必须有至少一条路径。如果这个约束条件不满足,那么这个连通块就不是连通的。 这个问题可以用Python的PuLP模块进行求解,代码如下: ``` python from pulp import * # 构建模型 model = LpProblem("Weighted Graph Partition", LpMinimize) # 定义变量 x = LpVariable.dicts("x", [(i, j) for i in range(1, n+1) for j in range(i+1, n+1)], cat=LpBinary) y = LpVariable.dicts("y", [i for i in range(1, n+1)], lowBound=1, upBound=k, cat=LpInteger) # 定义目标函数 model += lpSum([w[(i, j)] * x[(i, j)] for i in range(1, n+1) for j in range(i+1, n+1)]) # 定义约束条件 for i in range(1, n+1): model += lpSum([x[(i, j)] for j in range(i+1, n+1)]) == 1 for j in range(1, k+1): model += lpSum([y[i] == j for i in range(1, n+1)]) == 1 for i in range(1, n+1): for j in range(i+1, n+1): model += x[(i, j)] <= lpSum([x[(i, l)] for l in range(1, n+1) if l != j]) model += x[(i, j)] <= lpSum([x[(j, l)] for l in range(1, n+1) if l != i]) for i in range(1, n+1): model += y[i] >= y[max([j for j in range(1, i) if (j, i) in E]+[0])] # 求解模型 model.solve() # 输出结果 for i in range(1, n+1): print("Node %d is in partition %d" % (i, value(y[i]))) ``` 其中,w是一个字典,表示边的权值;E是一个边的集合,每条边用一个二元组表示。在求解模型之前,需要将w和E从原始的数据结构转换成字典的形式。 ### 回答2: 利用Python对加权图的连通划分混合整数线性规划模型,可以通过以下步骤实现: 1. 定义决策变量: - 设定一个二进制变量x[i, j],表示节i和节j是否连通,其中i和j分别表示图中的节。 - 设定一个连续变量y[i],表示节i的划分标志,用于区分不同的连通分量。 2. 定义目标函数: - 构建目标函数,权重之和最小化或距离之和最小化,即minimize sum(weights[i, j]*x[i, j])或minimize sum(distances[i, j]*x[i, j]),其中weights[i, j]表示节i和节j之间的权重或距离。 3. 定义约束条件: - 确保每个节都被划分到唯一的连通分量中,即sum(x[i, j] for j in nodes) = 1,其中nodes表示图中的所有节。 - 确保节i和节j连通的约束条件,即y[i] - y[j] + nx[i, j] >= 0,其中nx[i, j]表示节i和节j连通的二进制变量。 4. 设置决策变量的类型: - 定义x[i, j]为二进制变量,即x[i, j] in {0, 1}。 - 定义y[i]为连续变量。 5. 调用线性规划库进行求解: - 导入线性规划库(如PuLP、Gurobi等)。 - 定义模型对象。 - 添加目标函数和约束条件。 - 指定求解方法和求解参数。 - 求解模型并获取最优解。 利用Python编程语言,可以使用PuLP进行线性规划模型的建模和求解。首先,需要导入PuLP库,然后按照以上步骤建立模型,设置变量的类型,并添加目标函数和约束条件。接下来,指定求解方法和求解参数,最后调用求解器进行求解。最优解可以通过访问变量的值属性来获取。 以上是利用Python对加权图的连通划分混合整数线性规划模型的简要介绍。具体的实现过程可能因具体问题而有所不同,可以根据具体情况进行调整和扩展。 ### 回答3: 加权图的连通划分问题是在一个加权无向图中,找到一种划分方式,使得划分后的子图之间的边权重之和最小。而混合整数线性规划模型可以用来解决这个问题。 首先,我们定义一个布尔变量x,表示图中的每个结是否被划分到划分集合S。如果x=1,则该结在S中;如果x=0,则该结在剩余的结集合中。 接下来,我们定义一个整数变量y(i,j),表示边(i,j)的连通情况。如果边(i,j)跨越划分集合S和剩余集合,则y(i,j)=1;否则,y(i,j)=0。 然后,我们可以用如下的目标函数来表示最小化划分集合S和剩余集合之间边权值之和: min ∑w(i,j) * y(i,j) 其中(i,j)表示图的边,w(i,j)表示边(i,j)的权重。 需要满足的约束条件有: 1. 每个结必须被划分到一个集合中:∑x(i) = 1,其中i表示图中的每个结。 2. 边的连通性:y(i,j) ≥ x(i) - x(j),表示若边(i,j)跨越划分集合与剩余集合,则y(i,j)=1。 3. 集合S的规模限制:∑x(i) ≤ |V|/2,其中|V|表示图的结数。 最后,使用python的优化库,将该问题转化为线性规划问题,利用求解器求解该线性规划问题,得到最优解。 总之,利用python可以建立加权图的连通划分混合整数线性规划模型,通过求解器得到图的最优划分方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值