描述
给一个数组nums
, 对于里面的任意两个数字a
和b
, 如果a & b != 0
, 说明a
和b
是连通的. (每个数字可以看做一个节点) 求这些数字组成的图中, 最小环的长度. 如果没有环, 返回-1
.
数组长度最大100000
.
思路
如果所有节点两两判断是否连通, 复杂度肯定吃不消. 考虑所有数字(都是64位), 每一位可以看做是用来和其他数字连通的接口,
- 比如第一位(最低位), 如果所有数字中, 有
>=3
个数字在这一位是1
, 那么最小环的长度一定是3
. - 如果对于某一位, 所有数字中, 这一位全是
0
, 那么相当于这个接口没有被用到. 两个数字a
和b
就算连通, 也不是因为这一位的贡献. - 如果对于某一位, 所有数字中, 只有一个数字在这一位是
1
, 其他全是0
, 那么也相当于这个接口没有被用到(没有和它匹配的数字). - 所以, 真正有价值的信息是出现了正好
2
次的位中. 64位中, 就算出现2
次bit1
的位置完全分散开, 也只有64*2=128
个数字. 对于这最多128
个数字, 可以用floyd算法找最小环. 复杂度O(n**3)
可以接受.
关于WA了一次的floyd
为什么要写成下面这样, 而不能更新完多源最短路径, 再求最小环呢?
1 | for k in range(nv): # k是中间点, 相当于[i]-----[k]-----[j]~[i](成环) |
最内层每一次更新res
和dis[i][j]
都是针对上一层k-1
的值来说的. 也就是对于环内节点最大是k
的环来说, i
和j
的距离, 以及最小环长度. 如果完全更新完dis
矩阵, 那么dis[i][j]
这条最短路可能包含了当前的k
, 再和dis[i][k]
, dis[k][j]
相加没有意义.
上面其实也是floyd算法求(无向图)最小环的模板代码. (k
为什么写在最外面?: https://www.zhihu.com/question/30955032)
代码
1 | def do(): |