无向图求三元环

本文介绍一种高效算法来计算给定稀疏无向图中的三元环数量,通过巧妙的数据结构和枚举策略,将复杂度降低到O(m乘以根号m)。适用于点数不超过10万的图。

无向图是一个稀疏图,点n<105n<105 边数m<min(105,n×(n1)2)m<min(105,n×(n−1)2),现在给出n,m,E(边集合),求出这个三元环的个数,两个三元环不同是:当于对两个三元环的边,某个三元环存在一条边在另一个三元环中无法找到。
解法:暴力的方法可以使复杂度达到O(m×m)O(m×m).具体解法如下:
首先我们先是记录,使用邻接表记录无向图,记录每个点的出度,每条边用hash存下来,这个如何存呢,可以把边转换成一个独一无二的数,假设这条边端点是ab,可以存下a×n+ba×n+bb×n+ab×n+a,用set,map存都可以,这样如果我们得到两个点看是否相连,就看在set中是否能找到这个数。
储存过程结束后,开始从1-n枚举每个点设为x,然后找到所有和x相连的点记为y,并用link数组记录下link[y] = x,(此时相当于枚举了一条边)因为要找三元环,如果存在,另两个点肯定是和x相连的,这个时候分成两类
一:如果y点出度小于等于mm,那么就直接继续找和y相连的点,如果和y相连的点z发现 link[z] = x,说明这个点z也是和x联通的,“然后找到所有和x相连的点记为y,并用link数组记录下link[y] = x”在z点实际是在这一步标记的。这样我们就是找到一个个数加一
二:如果y点出度大于mm,那么我们从x的基础上,继续找一个点z,看z和y是否相连,判断方法就是计算z×n+yz×n+y 是否可以在set中找到,如果找到个数加一。
最终就完成了寻找,但是每个点都查找重复了三次,最后结果需要除以3。
复杂度分析
为了方便说明我们按照上面叙述的算法,记枚举的第一个点为x,第二个点为y,最后一个点为z,
枚举的边用两端点表示如枚举的第一条边xy
下面进行分类讨论:
第一类点y的出度<=mm,那么我们便继续枚举y的连接点z,这时候我们最多枚举的xy边(第一条边)有m条,每枚举完第一条边后,因为y的出度<=mm,所以在枚举完第一条边的基础上,最多每次枚举mm条边,所以此时的复杂度为O(mmm)
第二类点y的出度>mm,此时我们不在y点向外找点,我们回到x,找到另个与x相连的点z,此时找两个点所花费的复杂度大约是mm*mm=m,因为这样的y点不会超过mm个,所以总的复杂度就是O(mmm)
综上所述复杂度是O(mmm)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
vector<int>G[maxn];
set<ll>st;
int vis[maxn],link[maxn],out[maxn];
int main(){
    ll ans,sum;
    int n,m,i,j,k,x,y,z,B;
    while(scanf("%d%d",&n,&m) != EOF){
        //初始化
        B = sqrt(m);
        st.clear();
        for(i = 1; i <= n; i++){
            G[i].clear();
            vis[i] = out[i] = link[i] = 0;
        }
        for(i = 1; i <= m; i++){
            scanf("%d%d",&x,&y);
            G[x].push_back(y);
            out[x]++;
            G[y].push_back(x);
            out[y]++;
            st.insert((ll)x*n+y);//hash存边
            st.insert((ll)y*n+x);
        }
        ans = 0;
        for(i = 1; i <= n; i++){//枚举第一个点
            x = i;
            vis[x] = 1;//标记
            for(j = 0; j < G[x].size(); j++)
                link[G[x][j]] = x;
            for(j = 0; j < G[x].size(); j++){
                sum = 0;
                y = G[x][j];//枚举第二个点,枚举了第一条边
                if(vis[y])
                    continue;
                if(out[y] <= B){//第一类
                    for(k = 0; k < G[y].size(); k++){
                        z = G[y][k];
                        if(link[z] == x)
                            sum++;
                    }
                }
                else{//第二类
                    for(k = 0; k < G[x].size(); k++){
                        z = G[x][k];
                        if(st.find((ll)z*n+y) != st.end())
                            sum++;
                    }
                }
                ans += sum;
            }
        }
        printf("%lld\n",ans/3);
    }
    return 0;
}
有向无图(Directed Acyclic Graph,简称DAG)的邻接矩阵是一个 $ n \times n $ 的矩阵,其中 $ n $ 是图中顶点的数量。在邻接矩阵中,若存在从顶点 $ i $ 到顶点 $ j $ 的有向边,则矩阵中的元素 $ A[i][j] $ 为1(或边的权重),否则为0。由于是有向图,邻接矩阵通常不是对称的,因此不能像无向图那样仅通过存储上三角或下三角来实现压缩存储。 然而,对于某些特定的稀疏有向无图,如果边的数量远小于 $ n^2 $,可以采用以下方法进行邻接矩阵的压缩存储: ### 1. 使用三元组表示法 三元组表示法是一种常见的稀疏矩阵压缩方法。对于邻接矩阵中的每一个非零元素,记录其行索引、列索引和值。例如,可以使用一个列表或数组来存储所有非零元素的三元组 $(i, j, value)$。这种方法适用于稀疏图,因为它仅存储非零元素的信息,从而节省大量空间。 ### 2. 使用压缩稀疏行(CSR)格式 压缩稀疏行(Compressed Sparse Row,CSR)格式是一种高效的稀疏矩阵存储方式。它使用三个一维数组: - **values**:存储所有非零元素的值; - **columns**:存储每个非零元素对应的列索引; - **row_ptr**:指示每一行的起始位置在 `values` 和 `columns` 中的位置。 CSR 格式特别适合按行访问的运算,例如矩阵向量乘法。 ### 3. 使用压缩稀疏列(CSC)格式 压缩稀疏列(Compressed Sparse Column,CSC)与CSR类似,但它是按列组织的。它也使用三个数组: - **values**:存储所有非零元素的值; - **rows**:存储每个非零元素对应的行索引; - **col_ptr**:指示每一列的起始位置。 CSC 格式适合按列访问的操作。 ### 4. 哈希表存储 对于动态变化的图结构,可以使用哈希表来存储邻接矩阵中的非零元素。键可以是 $(i, j)$ 形式的元组,表示从顶点 $ i $ 到顶点 $ j $ 的边是否存在或其权重。这种方法在插入和删除操作频繁时具有较高的灵活性。 ### 示例代码(三元组表示法) ```python # 有向无图的三元组表示法 edges = [ (0, 1, 5), # 从顶点0到顶点1的边,权重为5 (0, 2, 3), # 从顶点0到顶点2的边,权重为3 (1, 3, 2), # 从顶点1到顶点3的边,权重为2 (2, 3, 4) # 从顶点2到顶点3的边,权重为4 ] # 构建邻接矩阵的压缩表示 adj_matrix_compressed = {} for i, j, weight in edges: if i not in adj_matrix_compressed: adj_matrix_compressed[i] = {} adj_matrix_compressed[i][j] = weight # 输出压缩后的邻接矩阵 print(adj_matrix_compressed) ``` ### 总结 对于有向无图的邻接矩阵,由于其通常不是对称的,因此不能像无向图那样通过存储上三角或下三角来压缩[^3]。不过,可以利用稀疏矩阵的压缩技术,如三元组表示、CSR、CSC 或哈希表,来高效地存储图的结构,特别是在边的数量远小于 $ n^2 $ 的情况下。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值