深度优先搜索:首先从任意节点 x 开始进行第一次深度优先搜索,到达距离其最远的节点,记为 y,然后再从 y 开始做第二次深度优先搜索,到达距离 y 最远的节点,记为 z,则 \delta(y,z) 即为树的直径,节点 y 与 节点 z 之间的距离即为直径的长度。
方法一:动态规划
思路与算法
题目要求找到城市间最大距离恰好为 d 的所有子树数目,子树中的任意两个节点的最大距离即为「树的直径 」。根据题意可以知道城市是一个含有 n 个节点的无向连通图,且该图中只含有 n-1 条边,最大距离为 d 子树需要满足以下条件:
该子树为该无向图中的一个连通单元;
该子树的直径长度为 d;
根据以上提示,我们只需计算出图中所有连通子树的直径即可。由于图中的节点数 n 的取值范围为 1 \le n \le 16,我们采用状态压缩的思想,用二进制掩码 mask 来表示图中的一个子树,枚举所有可能的子树,并检测该子树的连通性。如果该子树为一个连通单元,则计算该子树的直径即可。对于枚举的子树都需要进行如下计算:
constdfs = (root, adj) => { let first = 0, second = 0; mask &= ~(1 << root); for (const vertex of adj[root]) { if ((mask & (1 << vertex)) !== 0) { mask &= ~(1 << vertex); const distance = 1 + dfs(vertex, adj); if (distance > first) { second = first; first = distance; } elseif (distance > second) { second = distance; } } } diameter = Math.max(diameter, first + second); return first; };
constnumberOfLeadingZeros = (i) => { if (i === 0) return32; let n = 1; if (i >>> 16 === 0) { n += 16; i <<= 16; } if (i >>> 24 === 0) { n += 8; i <<= 8; } if (i >>> 28 === 0) { n += 4; i <<= 4; } if (i >>> 30 === 0) { n += 2; i <<= 2; } n -= i >>> 31; return n; }
publicint[] countSubgraphsForEachDiameter(int n, int[][] edges) { List<Integer>[] adj = newList[n]; for (inti=0; i < n; i++) { adj[i] = newArrayList<Integer>(); } for (int[] edge : edges) { intx= edge[0] - 1; inty= edge[1] - 1; adj[x].add(y); adj[y].add(x); }
int[] ans = newint[n - 1]; for (inti=1; i < (1 << n); i++) { intx=32 - Integer.numberOfLeadingZeros(i) - 1; intmask= i; inty= -1; Queue<Integer> queue = newArrayDeque<Integer>(); queue.offer(x); mask &= ~(1 << x); while (!queue.isEmpty()) { y = queue.poll(); for (int vertex : adj[y]) { if ((mask & (1 << vertex)) != 0) { mask &= ~(1 << vertex); queue.offer(vertex); } } } if (mask == 0) { intdiameter= dfs(adj, -1, y, i); if (diameter > 0) { ans[diameter - 1]++; } } } return ans; }
publicintdfs(List<Integer>[] adj, int parent, int u, int mask) { intdepth=0; for (int v : adj[u]) { if (v != parent && (mask & (1 << v)) != 0) { depth = Math.max(depth, 1 + dfs(adj, u, v, mask)); } } return depth; } }
staticinlineintmax(int a, int b) { return a > b ? a : b; }
intdfs(int parent, int u, int mask, int **adj, int *adjColSize) { int depth = 0; for (int i = 0; i < adjColSize[u]; i++) { int vertex = adj[u][i]; if (vertex != parent && mask & (1 << vertex)) { depth = max(depth, 1 + dfs(u, vertex, mask, adj, adjColSize)); } } return depth; }
int* countSubgraphsForEachDiameter(int n, int** edges, int edgesSize, int* edgesColSize, int* returnSize) { int *adj[n]; int adjColSize[n]; for (int i = 0; i < n; i++) { adj[i] = (int*)calloc(n, sizeof(int));; } memset(adjColSize, 0, sizeof(adjColSize)); for (int i = 0; i < edgesSize; i++) { int x = edges[i][0] - 1; int y = edges[i][1] - 1; adj[x][adjColSize[x]++] = y; adj[y][adjColSize[y]++] = x; }
int *ans = (int *)calloc(n - 1, sizeof(int)); for (int i = 1; i < (1 << n); i++) { int root = 32 - __builtin_clz(i) - 1, mask = i; intqueue[n]; int head = 0, tail = 0; int node = -1; queue[tail++] = root; mask &= ~(1 << root); while (head != tail) { node = queue[head++]; for (int j = 0; j < adjColSize[node]; j++) { int vertex = adj[node][j]; if (mask & (1 << vertex)) { mask &= ~(1 << vertex); queue[tail++] = vertex; } } } if (mask == 0) { int diameter = dfs(-1, node, i, adj, adjColSize); if (diameter > 0) { ans[diameter - 1]++; } } } for (int i = 0; i < n; i++) { free(adj[i]); } *returnSize = n - 1; return ans; }
var countSubgraphsForEachDiameter = function(n, edges) { const adj = newArray(n).fill(0); for (let i = 0; i < n; i++) { adj[i] = []; } for (const edge of edges) { const x = edge[0] - 1; const y = edge[1] - 1; adj[x].push(y); adj[y].push(x); }
const ans = newArray(n - 1).fill(0); for (let i = 1; i < (1 << n); i++) { const x = 32 - numberOfLeadingZeros(i) - 1; let mask = i; let y = -1; const queue = [x]; mask &= ~(1 << x); while (queue.length) { y = queue.shift(); for (const vertex of adj[y]) { if ((mask & (1 << vertex)) !== 0) { mask &= ~(1 << vertex); queue.push(vertex); } } } if (mask === 0) { const diameter = dfs(adj, -1, y, i); if (diameter > 0) { ans[diameter - 1]++; } } } return ans; }
constdfs = (adj, parent, u, mask) => { let depth = 0; for (const v of adj[u]) { if (v !== parent && (mask & (1 << v)) !== 0) { depth = Math.max(depth, 1 + dfs(adj, u, v, mask)); } } return depth; } constnumberOfLeadingZeros = (i) => { if (i === 0) return32; let n = 1; if (i >>> 16 === 0) { n += 16; i <<= 16; } if (i >>> 24 === 0) { n += 8; i <<= 8; } if (i >>> 28 === 0) { n += 4; i <<= 4; } if (i >>> 30 === 0) { n += 2; i <<= 2; } n -= i >>> 31; return n; }
设两个节点 x,y 且满足 x < y,则此时我们以 x 为根节点开始构造直径为 dist}[x][y] 的子树:
如果以当前相邻的节点 u 满足 dist}[u][x] < \textit{dist}[x][y],\textit{dist}[u][y] < \textit{dist}[x][y],此时节点 u 一定可以加入到子树中,此时的直径即为 dist}[x][y],此时我们继续向外延伸到 u 的孩子节点;
如果以当前相邻的节点 u 满足 dist}[u][x] = \textit{dist}[x][y] 或者 dist}[u][y] = \textit{dist}[x][y] 时,此时 u 也一定可以加入到子树中,但可能存在重复计算的问题,为了防止重复计算,对于每对 (x,y) 且 x < y,如果加入新的节点 u 后,存在相等的直径 dist}[u][x] 或者 dist}[u][y],此时我们要求直径的两个端点之和一定要大于等于 x + y:
如果 dist}[x][u] = \textit{dist}[x][y],即 u 到较小的节点 x 的距离等于当前的直径,则此时要求 (u + x) \ge (x + y),即此时需要满足 u \ge y;
如果 dist}[y][u] = \textit{dist}[x][y],即 u 到较大的节点 y 的距离等于当前的直径,则此时要求 (u + y) \ge (x + y),即此时需要满足 u \ge x;
如果当前节点 u 可以加入时,如果节点 u 不在 x 到 y 的关键路径上时,此时节点 u 可以不在目标子树中,以节点 u 为根节点的子树为空,即空子树也计数为一种子树。
<,,,,,,,>
我们每次计算以 u 为根节点构成满足要求的子树的数目 count}(u),此时它们之间的关系为相乘。即假设根节点 x 含有 m 个孩子节点分别为 c_0,c_1,\cdots,c_{m-1,每个孩子节点可以构成的子树的数目为 count}(c_0),\textit{count}(c_1),\cdots,\textit{count}(c_{m-1}),则此时构成的子树的总数目为 \prod\limits_{i=0}^{m-1}\textit{count}(c_i)。枚举所有的节点对 (x,y) 且满足 x < y,并计算以 dist}[x][y] 为直径的子树数目,即可计算出所有符合要求的子树的数目。
classSolution { publicint[] countSubgraphsForEachDiameter(int n, int[][] edges) { List<Integer>[] adj = newList[n]; int[][] dist = newint[n][n]; for (inti=0; i < n; i++) { Arrays.fill(dist[i], Integer.MAX_VALUE); dist[i][i] = 0; } for (inti=0; i < n; i++) { adj[i] = newArrayList<Integer>(); } for (int[] edge : edges) { intx= edge[0] - 1; inty= edge[1] - 1; adj[x].add(y); adj[y].add(x); dist[x][y] = dist[y][x] = 1; } for (inti=0; i < n; i++) { for (intj=0; j < n; j++) { for (intk=0; k < n; k++) { if (dist[j][i] != Integer.MAX_VALUE && dist[i][k] != Integer.MAX_VALUE) { dist[j][k] = Math.min(dist[j][k], dist[j][i] + dist[i][k]); } } } } int[] ans = newint[n - 1]; for (inti=0; i < n - 1; i++) { for (intj= i + 1; j < n; j++) { ans[dist[i][j] - 1] += dfs(adj, dist, i, -1, i, j); } } return ans; }
publicintdfs(List<Integer>[] adj, int[][] dist, int u, int parent, int x, int y) { if (dist[u][x] > dist[x][y] || dist[u][y] > dist[x][y]) { return1; } if ((dist[u][y] == dist[x][y] && u < x) || (dist[u][x] == dist[x][y] && u < y)) { return1; } intret=1; for (int v : adj[u]) { if (v != parent) { ret *= dfs(adj, dist, v, u, x, y); } } if (dist[u][x] + dist[u][y] > dist[x][y]) { ret++; } return ret; } }
publicclassSolution { publicint[] CountSubgraphsForEachDiameter(int n, int[][] edges) { IList<int>[] adj = new IList<int>[n]; int[][] dist = newint[n][]; for (int i = 0; i < n; i++) { dist[i] = newint[n]; Array.Fill(dist[i], int.MaxValue); dist[i][i] = 0; } for (int i = 0; i < n; i++) { adj[i] = new List<int>(); } foreach (int[] edge in edges) { int x = edge[0] - 1; int y = edge[1] - 1; adj[x].Add(y); adj[y].Add(x); dist[x][y] = dist[y][x] = 1; } for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int k = 0; k < n; k++) { if (dist[j][i] != int.MaxValue && dist[i][k] != int.MaxValue) { dist[j][k] = Math.Min(dist[j][k], dist[j][i] + dist[i][k]); } } } } int[] ans = newint[n - 1]; for (int i = 0; i < n - 1; i++) { for (int j = i + 1; j < n; j++) { ans[dist[i][j] - 1] += DFS(adj, dist, i, -1, i, j); } } return ans; }
publicintDFS(IList<int>[] adj, int[][] dist, int u, int parent, int x, int y) { if (dist[u][x] > dist[x][y] || dist[u][y] > dist[x][y]) { return1; } if ((dist[u][y] == dist[x][y] && u < x) || (dist[u][x] == dist[x][y] && u < y)) { return1; } int ret = 1; foreach (int v in adj[u]) { if (v != parent) { ret *= DFS(adj, dist, v, u, x, y); } } if (dist[u][x] + dist[u][y] > dist[x][y]) { ret++; } return ret; } }
staticinlineintmin(int a, int b) { return a < b ? a : b; }
intdfs(int u, int parent, int x, int y, int **dist, int **adj, int *adjColSize) { if (dist[u][x] > dist[x][y] || dist[u][y] > dist[x][y]) { return1; } if ((dist[u][y] == dist[x][y] && u < x) || (dist[u][x] == dist[x][y] && u < y)) { return1; } int ret = 1; for (int i = 0; i < adjColSize[u]; i++) { int v = adj[u][i]; if (v != parent) { ret *= dfs(v, u, x, y, dist, adj, adjColSize); } } if (dist[u][x] + dist[u][y] > dist[x][y]) { ret++; } return ret; };
int* countSubgraphsForEachDiameter(int n, int** edges, int edgesSize, int* edgesColSize, int* returnSize) { int *adj[n], *dist[n]; int adjColSize[n]; for (int i = 0; i < n; i++) { adj[i] = (int *)calloc(n, sizeof(int)); dist[i] = (int *)calloc(n, sizeof(int)); memset(dist[i], 0x3f, sizeof(int) * n); dist[i][i] = 0; } memset(adjColSize, 0, sizeof(adjColSize)); for (int i = 0; i < edgesSize; i++) { int x = edges[i][0] - 1; int y = edges[i][1] - 1; adj[x][adjColSize[x]++] = y; adj[y][adjColSize[y]++] = x; dist[x][y] = dist[y][x] = 1; } for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int k = 0; k < n; k++) { if (dist[j][i] != INFI && dist[i][k] != INFI) { dist[j][k] = min(dist[j][k], dist[j][i] + dist[i][k]); } } } }
int *ans = (int *)calloc(n - 1, sizeof(int)); for (int i = 0; i < n - 1; i++) { for (int j = i + 1; j < n; j++) { ans[dist[i][j] - 1] += dfs(i, -1, i, j, dist, adj, adjColSize); } } for (int i = 0; i < n; i++) { free(adj[i]); free(dist[i]); } *returnSize = n - 1; return ans; }
var countSubgraphsForEachDiameter = function(n, edges) { const adj = newArray(n).fill(0); const dist = newArray(n).fill(0).map(() =>newArray(n).fill(Number.MAX_SAFE_INTEGER)); for (let i = 0; i < n; i++) { dist[i][i] = 0; } for (let i = 0; i < n; i++) { adj[i] = []; } for (const edge of edges) { const x = edge[0] - 1; const y = edge[1] - 1; adj[x].push(y); adj[y].push(x); dist[x][y] = dist[y][x] = 1; } for (let i = 0; i < n; i++) { for (let j = 0; j < n; j++) { for (let k = 0; k < n; k++) { if (dist[j][i] !== Number.MAX_VALUE && dist[i][k] !== Number.MAX_VALUE) { dist[j][k] = Math.min(dist[j][k], dist[j][i] + dist[i][k]); } } } } const ans = newArray(n - 1).fill(0); for (let i = 0; i < n - 1; i++) { for (let j = i + 1; j < n; j++) { ans[dist[i][j] - 1] += dfs(adj, dist, i, -1, i, j); } } return ans; }
constdfs = (adj, dist, u, parent, x, y) => { if (dist[u][x] > dist[x][y] || dist[u][y] > dist[x][y]) { return1; } if ((dist[u][y] === dist[x][y] && u < x) || (dist[u][x] === dist[x][y] && u < y)) { return1; } let ret = 1; for (const v of adj[u]) { if (v !== parent) { ret *= dfs(adj, dist, v, u, x, y); } } if (dist[u][x] + dist[u][y] > dist[x][y]) { ret++; } return ret; } constnumberOfLeadingZeros = (i) => { if (i === 0) return32; let n = 1; if (i >>> 16 === 0) { n += 16; i <<= 16; } if (i >>> 24 === 0) { n += 8; i <<= 8; } if (i >>> 28 === 0) { n += 4; i <<= 4; } if (i >>> 30 === 0) { n += 2; i <<= 2; } n -= i >>> 31; return n; }