1617. 统计子树中城市之间最大距离
题目描述
有n个城市,编号是1-n,
给了一个数组edges表示了城市的连接关系,且每个城市间最多只有一条路(城市间的连接是树),
需要求一个结果ans,其中ans[i]表示城市间最大距离是i+1子树的数目
f1-状态压缩+动态规划 |
基本分析
- 城市个数不超过15可以暗示什么?用二进制表示当前城市的选择状态
- 怎么定义dp状态?dp[j]表示城市选择是j时候对应的最大距离
- 以上状态j一定都是合法的吗,怎么判断?j可能本身不能联通,(1)在利用j进行搭建的时候就判断一下,如果f[j]是0,表示不能搭建;(2)对需要枚举新增的节点i,看j+(1<<i)是不是能联通?做法是枚举j中的节点k,看是不是存在d[k][i]=1的点
- 怎么考虑转移?如果nj是联通的,这个时候就遍历j内的所有点,找出和i点距离最大的更新f[nj]
- 怎么取得最后的结果?f[j]的含子树是j时候的最大距离,那么遍历j,只要f[j]不等于0(对应单个点或者不成立的子树),f[j]-1的值就是ans的索引,累加ans[f[j]-1]就行
代码
class Solution: def countSubgraphsForEachDiameter(self, n: int, edges: List[List[int]]) -> List[int]: # 定义距离d d = [[n+1]* n for _ in range(n)] # 定义dp数组f mask = 1<<n f = [0] * mask # 相同点的d值设置为0 for i in range(n): for j in range(n): if i == j: d[i][j] = 0 # 遍历相邻点,初始化d和一部分子树 for x, y in edges: d[x-1][y-1] = 1 d[y-1][x-1] = 1 f[(1<<(x-1)) + (1<<(y-1))] = 1 # Floyed初始化之间的最小距离 for k in range(n): for i in range(n): for j in range(n): if d[i][k] != n+1 and d[k][j]!= n+1: d[i][j] = min(d[i][j], d[i][k]+d[k][j]) # 遍历状态j for j in range(1, mask): # 去掉不是树的状态,通过f判断 if f[j] == 0: continue # 遍历可能能新增的节点i,对i的要求是i不在j中,且新状态没有被计算过 for i in range(n): nj = j + (1<<i) if j & (1<<i) != 0 or f[nj] != 0: continue # 遍历j,看是不是能连起来 for k in range(n): if j & (1<<k) != 0 and d[k][i] ==1: f[nj] = f[j] break # 如果新状态不能存在,不考虑i了 if f[nj] == 0: continue # 再次遍历j中的节点,来维护dp距离 for k in range(n): if j & (1<<k) != 0: f[nj] = max(f[nj], d[k][i]) # 统计结果 ans = [0] * (n-1) for j in range(1, mask): if f[j] != 0: ans[f[j]-1] += 1 return ans
复杂度
时间:
空间:
总结
- 几个边界需要注意:
- (1)节点自身间的距离是
- (2)相邻节点间的距离是1
- (3)相邻节点构成了最小子树,对应的f值是1
- (4)孤立的节点和不能成子树的节点组合对应的f值都是0
- (5)判断一个组合j是不是有效,就是(1)先看对应的f值是不是0;(2)对可能的新组合nj,如果找不到k可以给nj赋值,那么nj还是0,再转移前就会退出当前对i的讨论
- 预处理的时候,用Floyed获取任意点间的最小距离
- 枚举状态的时候需要考虑:
- (1)当前j可能是不合法的,这个时候就换下一个j,或者是合法的往下走;
- (2)枚举需要扩展的节点i,对i的要求是<1>i不能被j包含+<2>j+i节点之前没有算过,如果满足以上要求,这个i才去判断是不是可以连接;
- (3)怎么判断连接?j中存在一个点k和i的距离是1,如果没有的话怎么办?说明j+(1<<i)这个子树不成立,去找一下i;
- (4)怎么更新状态,再次遍历j中的点k,每次取f[nj]和d[i][k]的最大值
- 求结果的时候就是遍历f,生成答案
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现