Problem: 2646. 最小化旅行的价格总和
思路
看到示例一的第二个图,是否想起某个让人入门树形DP的时候抓狂的题——打家劫舍。
是的,但也不太一样。
我们先思考如何简单的求出旅行需要的价格(价格不减),这个问题就是求树中两节点之间的最短路径,将多个路径加起来,就是本问题的答案。只不过这里我们是为了用 count
统计某个节点走过的总次数。
接着我们思考如果价格减半,我们最多能省多少钱?
也就是用 count
来更新 price
即 price[i] *= count[i]
,以用新的 price
来表示当前节点的权值。我们所要求的就是我们能减多少价格。
对于当前节点价格是否减半,我们有且仅有两个选择:减或不减。
我们从抽离出一个节点,他含有 n 个子节点:
减半:此时他的所有子节点的状态是不减半,即此时减半的总价格为:
∑ c h i l d ( 不减半 ) + 当前节点减半的价格 ( c o u n t [ c u r r ] ∗ p r i c e [ c u r r ] / 2 ) \sum child(不减半) + 当前节点减半的价格(count[curr]*price[curr]/2) ∑child(不减半)+当前节点减半的价格(count[curr]∗price[curr]/2)
不减半:此时当前节点对子节点状态无影响,我们只需要求出子节点选或不选的最大值之和即可:
∑ m a x { c h i l d ( 减半 ) , c h i l d 不减半 ( ) } \sum max \lbrace child(减半), child不减半() \rbrace ∑max{child(减半),child不减半()}
接着我们只需要返回 最大价格-最多省钱
就是我们的最小价格。
解题方法
- 采用邻接表构建树,注意此时的双向的;
- 递归旅行中每个节点出现的次数
count[i]
; - 求当前能剩下的最大钱数;
- 求旅行需花费的最大金额;
- 返回
最大金额-剩下的最大钱数
。
复杂度
时间复杂度: O ( m n ) O(mn) O(mn), m m m为trips的大小, n n n为节点个数
空间复杂度: O ( n ) O(n) O(n)
Code
class Solution {
public:
bool countDFS(vector<vector<int>> &vertex, int curr, int end, int parent, int count[]) {
if (curr == end) {
count[curr]++;
return true;
}
for (auto &child: vertex[curr]) {
if (parent == child) continue;
if (countDFS(vertex, child, end, curr, count)) {
count[curr]++;
return true;
}
}
return false;
}
vector<int> dpDFS(vector<vector<int>> &vertex, int curr, int parent, int count[], vector<int> &price) {
int sel = 0, non_sel = 0;
for (auto &child: vertex[curr]) {
if (parent == child) continue;
vector<int> rec = dpDFS(vertex, child, curr, count, price);
sel += max(rec[0], rec[1]);
non_sel += rec[1];
}
return {non_sel+count[curr]*price[curr]/2, sel};
}
int minimumTotalPrice(int n, vector<vector<int>>& edges, vector<int>& price, vector<vector<int>>& trips) {
vector<vector<int>> vertex(n);
int count[n];
int maxPrice = 0;
memset(count, 0, sizeof(count));
for (auto edge: edges) {
vertex[edge[0]].push_back(edge[1]);
vertex[edge[1]].push_back(edge[0]);
}
for (auto trip: trips) {
countDFS(vertex, trip[0], trip[1], -1, count);
}
vector<int> rec = dpDFS(vertex, 0, -1, count, price);
for (int i=0; i<n; i++) {
maxPrice += count[i] * price[i];
}
return maxPrice - max(rec[0], rec[1]);
}
};