道题要求每个节点到其他所有节点的距离之和
先考虑一个节点到其他所有节点的距离之和的问题。看到距离首先我想到的就是最短路,但是树上没啥最短路_(:з」∠)_
然后我就没想出什么行之有效的方法_(:з」∠)_
看了题解是树形dp,那么问题来了,为什么呢?这道题也没有什么最值,看起来距离之和是一个很固定的东西,一点都不动态。
其实我觉得,与其说是dp,不如说是递推。这里的中心思想就是从叶子往上推,处理距离
distSum[root] = sigma(distSum[root'sChild] + childSum[root'sChild]
将这个递推用递归写出来
void dfs(int node, int fa)
{
distSum[node] = 0;
childNum[node] = 1;
for (int i = 0; i < graph[node].size(); i++)
{
if (graph[node][i] == fa) continue;
dfs(graph[node][i], node);
childNum[node] += childNum[graph[node][i]];
distSum[node] += distSum[graph[node][i]] + childNum[graph[node][i]];
}
}
解决了这个问题之后,你可以把所有点都当做root跑一遍dfs,但是你看看根的儿子,它到所有节点的距离之和根本不用重新算,可以从父节点推出来。
distSum[node] = distSum[fa] - childNum[node] + (N - childNum[node]);
完整代码:
class Solution {
public:
vector<vector<int>> graph;
vector<int> distSum, childNum;
void dfs(int node, int fa)
{
distSum[node] = 0;
childNum[node] = 1;
for (int i = 0; i < graph[node].size(); i++)
{
if (graph[node][i] == fa) continue;
dfs(graph[node][i], node);
childNum[node] += childNum[graph[node][i]];
distSum[node] += distSum[graph[node][i]] + childNum[graph[node][i]];
}
}
void changeRoot(int node, int fa)
{
if (fa != -1)
{
distSum[node] = distSum[fa] - childNum[node] + (graph.size() - childNum[node]);
}
for (int i = 0; i < graph[node].size(); i++)
{
if (graph[node][i] == fa) continue;
changeRoot(graph[node][i], node);
}
}
vector<int> sumOfDistancesInTree(int N, vector<vector<int>>& edges) {
graph.resize(N, {});
childNum.resize(N, 0);
distSum.resize(N, 0);
for (int i = 0; i < edges.size(); i++)
{
graph[edges[i][0]].push_back(edges[i][1]);
graph[edges[i][1]].push_back(edges[i][0]);
}
dfs(0, -1);
changeRoot(0, -1);
return distSum;
}
};
另,看到了两篇蛮不错的树形dp讲解
这篇的重点是树形dp在设状态转移方程时都可以用f[i][]表示i这颗子树怎么怎么样的最优解,实现时一般都是用子树更新父亲
这篇的重点是给出了树形dp的解题步骤:1、判断是否是树规题 2、建树 3、写树规方程