算法学习笔记(Floyd进阶应用——传递闭包)

Floyd点这里

传递闭包的概念:

给定一个集合,以及若干元素的传递关系,传递闭包问题是求解所有元素的联通关系,如给定集合 a , b , c {a,b,c} a,b,c,已知 a a a -> b b b, b b b -> c c c,我们可以推出 a a a -> c c c

如果是借已知单向图推两点间的联通关系,可以用 D F S DFS DFS B F S BFS BFS复杂度为 O ( n + m ) O(n + m) O(n+m)。但是求解传递闭包问题时,我们需要求得所有点对之间的联通关系,复杂度就达到了 O ( n ( n + m ) ) O(n(n + m)) O(n(n+m)),在稠密图中,复杂度约等于 O ( n 3 ) O(n^3) O(n3),这么高的复杂度,我们可以用相同复杂度但是代码简单的 F l o y d Floyd Floyd进行处理。

具体方案是用点对间的联通关系来代替原本的路径长度进行递推。

d p [ i ] [ k ] = 1 dp[i][k] = 1 dp[i][k]=1并且 d p [ k ] [ j ] = 1 dp[k][j] = 1 dp[k][j]=1(等于 1 1 1表示两节点联通),则说明节点 i i i可以借由节点 k k k到达节点 j j j

动态转移方程就变成了:
d p [ i ] [ j ]    ∣ =   d p [ i ] [ k ]   &   d p [ k ] [ j ] dp[i][j]\ \ |= \ dp[i][k]\ \&\ dp[k][j] dp[i][j]  = dp[i][k] & dp[k][j]

传递闭包的Floyd代码:

for(int k = 1; k <= n; ++k)
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= n; ++j)
            dp[i][j] |= dp[i][k] & dp[k][j];

简单优化一下:先判断 d p [ i ] [ k ] dp[i][k] dp[i][k]是否为 1 1 1再进入 j j j的循环,可以减少一点计算时间。

for(int k = 1; k <= n; ++k)
    for(int i = 1; i <= n; ++i)
        if(dp[i][k])
            for(int j = 1; j <= n; ++j)
                if(dp[k][j])
                    dp[i][j] = 1;

复杂度还是 O ( n 3 ) O(n ^ 3) O(n3)

但实际上这个代码还能再进行优化,因为 d p dp dp数组中的值只有 0 0 0 1 1 1

而且最后一层里, d p [ k ] [ j ] dp[k][j] dp[k][j] 1 1 1时将 d p [ i ] [ j ] dp[i][j] dp[i][j]赋值为 1 1 1,这个操作其实可以用或操作来取代。所以终极的优化就是使用 b i t s e t bitset bitset,既可以存 0 0 0 1 1 1,还可以直接进行位运算。

具体代码如下:

bitset<N> dp[N];
void Floyd()
{
    for(int k = 1; k <= n; ++k)
        for(int i = 1; i <= n; ++i)
            if(d[i][k])
                d[i] |= d[k]; 
}

最后复杂度降为了 O ( n 2 ) O(n ^ 2) O(n2),优于直接搜索,可以处理 n = 1000 n = 1000 n=1000的数据量。

  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值