【图论】【状态压缩】【树】【深度优先搜索】1617. 统计子树中城市之间最大距离

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 【图论】【状态压缩】【树】【深度优先搜索】1617. 统计子树中城市之间最大距离

作者推荐

【动态规划】【字符串】【行程码】1531. 压缩字符串

本文涉及的知识点

图论 深度优先搜索 状态压缩 树

LeetCode1617. 统计子树中城市之间最大距离

给你 n 个城市,编号为从 1 到 n 。同时给你一个大小为 n-1 的数组 edges ,其中 edges[i] = [ui, vi] 表示城市 ui 和 vi 之间有一条双向边。题目保证任意城市之间只有唯一的一条路径。换句话说,所有城市形成了一棵 树 。

一棵 子树 是城市的一个子集,且子集中任意城市之间可以通过子集中的其他城市和边到达。两个子树被认为不一样的条件是至少有一个城市在其中一棵子树中存在,但在另一棵子树中不存在。

对于 d 从 1 到 n-1 ,请你找到城市间 最大距离 恰好为 d 的所有子树数目。

请你返回一个大小为 n-1 的数组,其中第 d 个元素(下标从 1 开始)是城市间 最大距离 恰好等于 d 的子树数目。

请注意,两个城市间距离定义为它们之间需要经过的边的数目。

示例 1:

输入:n = 4, edges = [[1,2],[2,3],[2,4]]

输出:[3,4,0]

解释:

子树 {1,2}, {2,3} 和 {2,4} 最大距离都是 1 。

子树 {1,2,3}, {1,2,4}, {2,3,4} 和 {1,2,3,4} 最大距离都为 2 。

不存在城市间最大距离为 3 的子树。

示例 2:

输入:n = 2, edges = [[1,2]]

输出:[1]

示例 3:

输入:n = 3, edges = [[1,2],[2,3]]

输出:[2,1]

提示:

2 <= n <= 15

edges.length == n-1

edges[i].length == 2

1 <= ui, vi <= n

题目保证 (ui, vi) 所表示的边互不相同。

深度优先搜索

编号从1到n,变成0到n-1。

枚举所有可能的子树mask,如果mask & (1 << i ) 表示第i个节点在此树中。

任意节点为根都可以,比如:0。

状态表示

m_vDis1[mask]记录 子树mask 以根节点为起点的最长路径经过的节点数。

m_vDis2[mask]记录子树mask 最长路径 经过的节点数。

非法子树两者都为0。

子树的的转移方程

子树的某合法状态:以它为根节点的子树。

枚举各子树的状态,处理独子树。独子树为mask,根节点为root,则当前树为:mask1 = mask | ( 1 << root)。

m_vDis1[mask1] = m_vDis1[mask]+1

m_vDis2[mask1] = max(m_vDis1[mask1] ,m_vDis2[mask])

处理非独子树

vLeft 记录根节点和前i棵子树 组成 的 所有合法字子树,如:mask1,第i棵子树的某合法状态 mask2。

两者合并后的新状态为mask3 = mask1 | mask2。

m_vDis1[mask3] = max(m_vDis1[mask1] ,m_vDis1[mask2]+1)

m_vDis2[mask3] = max(m_vDis2[mask1],m_vDis2[mask2],m_vDis1[mask1]+m_vDis1[mask2])

代码

核心代码

class CNeiBo2
{
public:
  CNeiBo2(int n, bool bDirect, int iBase = 0) :m_iN(n), m_bDirect(bDirect), m_iBase(iBase)
  {
    m_vNeiB.resize(n);
  }
  CNeiBo2(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0) :m_iN(n), m_bDirect(bDirect), m_iBase(iBase)
  {
    m_vNeiB.resize(n);
    for (const auto& v : edges)
    {
      m_vNeiB[v[0] - iBase].emplace_back(v[1] - iBase);
      if (!bDirect)
      {
        m_vNeiB[v[1] - iBase].emplace_back(v[0] - iBase);
      }
    }
  }
  inline void Add(int iNode1, int iNode2)
  {
    iNode1 -= m_iBase;
    iNode2 -= m_iBase;
    m_vNeiB[iNode1].emplace_back(iNode2);
    if (!m_bDirect)
    {
      m_vNeiB[iNode2].emplace_back(iNode1);
    }
  }
  const int m_iN;
  const bool m_bDirect;
  const int m_iBase;
  vector<vector<int>> m_vNeiB;
};
class Solution {
public:
  vector<int> countSubgraphsForEachDiameter(int n, vector<vector<int>>& edges) {
    m_vDis1.resize((1 << n));
    m_vDis2.resize((1 << n));
    CNeiBo2 neibo(n, edges, false, 1);
    DFS(neibo.m_vNeiB, 0, -1);
    vector<int> vRet(n-1);
    for (int i = 0; i < (1 << n); i++)
    {
      const int inx = m_vDis2[i] - 2;
      if (inx >= 0)
      {
        vRet[inx]++;
      }
    }
    return vRet;
  }
  vector<int> DFS(vector<vector<int>>& neiBo,int cur,int par)
  {
    vector<int> statu;
    const int curMask = 1 << cur;
    statu.emplace_back(curMask);
    m_vDis1[curMask] = m_vDis2[curMask] = 1;
    for (const auto& next : neiBo[cur])
    {
      if (next == par)
      {
        continue;
      }
      auto child = DFS(neiBo, next, cur);     
      const int iLeftCount = statu.size();
      for(const auto& mask : child )
      {
        {//处理独子树
          const int mask1 = mask | curMask;
          m_vDis1[mask1] = m_vDis1[mask] + 1;
          m_vDis2[mask1] = max(m_vDis1[mask1], m_vDis2[mask]);
          statu.emplace_back(mask1);
        }
        {//非独子树
          for (int i = iLeftCount - 1; i >= 0; i--)
          {
            const int mask1 = mask | statu[i];
            m_vDis1[mask1] = max(m_vDis1[statu[i]], m_vDis1[mask] + 1);
            m_vDis2[mask1] = max(m_vDis2[statu[i]], m_vDis2[mask]);
            m_vDis2[mask1] = max(m_vDis2[mask1], m_vDis1[statu[i]]+ m_vDis1[mask]);
            statu.emplace_back(mask1);
          }
        }
      }
    }
    return statu;
  }
  vector<int> m_vDis1, m_vDis2;
};

测试用例

template<class T>
void Assert(const T& t1, const T& t2)
{
  assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
  if (v1.size() != v2.size())
  {
    assert(false);
    return;
  }
  for (int i = 0; i < v1.size(); i++)
  {
    Assert(v1[i], v2[i]);
  }
}
int main()
{ 
  int n;
  vector<vector<int>> edges;
  
  {
    Solution sln;
    n = 2, edges = { {1,2} };
    auto res = sln.countSubgraphsForEachDiameter(n, edges);
    Assert(res, vector<int>{1});
  }
  {
    Solution sln;
    n = 4, edges = { {1,2},{2,3},{2,4} };
    auto res = sln.countSubgraphsForEachDiameter(n, edges);
    Assert(res, vector<int>{3, 4, 0});
  }
  {
    Solution sln;
    n = 3, edges = { {1,2},{2,3} };
    auto res = sln.countSubgraphsForEachDiameter(n, edges);
    Assert(res, vector<int>{2,1});
  }
  
}

2023年2月

class Solution {

public:

vector countSubgraphsForEachDiameter(int n, vector<vector>& edges) {

m_n = n;

m_vNear.resize(n);

for (const auto& v : edges)

{

m_vNear[v[0] - 1].push_back(v[1] - 1);

m_vNear[v[1] - 1].push_back(v[0] - 1);

}

DFSForDelParent(0, -1);

BSFForNodeSort(0, -1);

m_vNodeLeveMaxDist.assign(n, vector< vector>(n + 1, vector(n + 1)));

for (auto it = m_vSortNode.rbegin(); it != m_vSortNode.rend(); ++it)

{

dfs(*it);

}

vector vRet;

for (int iMaxDis = 1; iMaxDis < n; iMaxDis++)

{

int iSum = 0;

for (int iNode = 0; iNode < n; iNode++)

{

for (int leve = 0; leve <= n; leve++)

{

iSum += m_vNodeLeveMaxDist[iNode][leve][iMaxDis + 1];

}

}

vRet.push_back(iSum);

}

return vRet;

}

void DFSForDelParent(int iCur, const int iParent)

{

auto& v = m_vNear[iCur];

for (auto it = v.begin(); it != v.end(); ++ it )

{

if (iParent == *it )

{

v.erase(it);

break;

}

}

for (auto it = v.begin(); it != v.end(); ++it)

{

DFSForDelParent(*it, iCur);

}

}

void BSFForNodeSort(int iCur, const int iParent)

{

m_vSortNode.push_back(iCur);

int iNotSubNode = m_vSortNode.size();

for (const auto& next : m_vNear[iCur])

{

m_vSortNode.push_back(next);

}

const int iNodeNum = m_vSortNode.size();

for (int i = iNotSubNode; i < iNodeNum; i++)

{

BSFForNodeSort(m_vSortNode[i], iCur);

}

}

void dfs(int iCur)

{

vector<vector> pre(m_n + 1, vector(m_n + 1));

pre[0+1][-1+1] = 1;

for (const auto& next : m_vNear[iCur])

{

vector<vector> dp(m_n + 1, vector(m_n + 1));

for (int iPreLeve = -1; iPreLeve < m_n; iPreLeve++)

{

for (int iPreMaxDis = -1; iPreMaxDis < m_n; iPreMaxDis++)

{

if (0 == pre[iPreLeve + 1][iPreMaxDis + 1])

{

continue;

}

for (int iLeve = -1; iLeve < m_n; iLeve++)

{

for (int iMaxDis = -1; iMaxDis < m_n; iMaxDis++)

{

if (0 == m_vNodeLeveMaxDist[next][iLeve + 1][iMaxDis + 1])

{

continue;

}

int iNewMaxDis = max(iPreMaxDis, max(iMaxDis, iLeve));

iNewMaxDis = max(iNewMaxDis, iPreLeve + iLeve+1 );

int iNewLeve = max(iPreLeve, iLeve + 1);

dp[iNewLeve + 1][iNewMaxDis + 1] += pre[iPreLeve + 1][iPreMaxDis + 1] * m_vNodeLeveMaxDist[next][iLeve + 1][iMaxDis + 1];

}

}

}

}

pre.swap(dp);

}

pre[-1 + 1][-1 + 1]++;

m_vNodeLeveMaxDist[iCur] = pre;

}

vector m_vSortNode;//根节点,一级节点,二级节点…

vector<vector> m_vNear;

vector<vector<vector>> m_vNodeLeveMaxDist;

int m_n;

};

2023年9月

class Solution {

public:

vector countSubgraphsForEachDiameter(int n, vector<vector>& edges) {

CNeiBo2 neiBo(n, edges, false, 1);

AssignVector3(m_vLeveNums,n,n,n,0);

dfs(0, -1, neiBo);

vector vRet(n - 1);

for (int Dis = 1; Dis < n; Dis++)

{

for (int node = 0; node < n; node++)

{

for (int leve = 0; leve < n; leve++)

{

vRet[Dis - 1] += m_vLeveNums[node][leve][Dis];

}

}

}

return vRet;

}

void dfs(int cur,int parent, CNeiBo2& neiBo)

{

m_vLeveNums[cur][0][0] = 1;

for (const auto& next : neiBo.m_vNeiB[cur])

{

if (next == parent)

{

continue;

}

dfs(next, cur, neiBo);

auto pre = m_vLeveNums[cur];

for (int preLeve = 0; preLeve < pre.size(); preLeve++)

{

for (int preDis = 0; preDis < pre.size(); preDis++)

{

for (int nextLeve = 0; nextLeve + 1 < pre.size(); nextLeve++)

{

for (int nextDis = 0; nextDis < pre.size(); nextDis++)

{

const int iNewLeve = max(nextLeve + 1, preLeve);

int iNewDis = max(preDis, preLeve + nextLeve + 1);

MaxSelf(&iNewDis, nextDis);

if (iNewDis >= pre.size())

{

continue;

}

m_vLeveNums[cur][iNewLeve][iNewDis] += pre[preLeve][preDis] * m_vLeveNums[next][nextLeve][nextDis];

}

}

}

}

}

}

vector<vector<vector>> m_vLeveNums;//m_vRet[cur][i][j]表示以cur为根 ,叶子距离cur最大距离为i,任意两个节点最大距离为j。m_vRet[cur][0][0]表示只有根节点

};


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
7月前
|
测试技术
【动态规划】【组合数学】1866. 恰有 K 根木棍可以看到的排列数目
【动态规划】【组合数学】1866. 恰有 K 根木棍可以看到的排列数目
|
6月前
|
存储
第5章 图的遍历
第5章 图的遍历
|
7月前
|
机器学习/深度学习 算法 测试技术
【单源最短路 图论】882. 细分图中的可到达节点
【单源最短路 图论】882. 细分图中的可到达节点
|
7月前
|
算法 测试技术 C#
【树 图论 阶乘 组合 深度优先搜索】1916. 统计为蚁群构筑房间的不同顺序
【树 图论 阶乘 组合 深度优先搜索】1916. 统计为蚁群构筑房间的不同顺序
|
7月前
|
人工智能 算法 BI
【深度优先搜索 图论 树】2872. 可以被 K 整除连通块的最大数目
【深度优先搜索 图论 树】2872. 可以被 K 整除连通块的最大数目
|
7月前
|
人工智能 算法 BI
【图论】【树】 【拓扑排序】2603. 收集树中金币
【图论】【树】 【拓扑排序】2603. 收集树中金币
|
7月前
|
人工智能 算法 BI
【树】【因子数】【数论】【深度优先搜索】2440. 创建价值相同的连通块
【树】【因子数】【数论】【深度优先搜索】2440. 创建价值相同的连通块
|
7月前
|
算法 测试技术 C#
【图论】【分类讨论】LeetCode3017按距离统计房屋对数目
【图论】【分类讨论】LeetCode3017按距离统计房屋对数目
|
7月前
|
人工智能 算法 BI
【深度优先搜索】【C++算法】834 树中距离之和
【深度优先搜索】【C++算法】834 树中距离之和
|
7月前
|
算法 Java 测试技术
【广度优先搜索】【网格】【割点】1263. 推箱子
【广度优先搜索】【网格】【割点】1263. 推箱子