Given an undirected tree consisting of n
vertices numbered from 0
to n-1
, which has some apples in their vertices. You spend 1 second to walk over one edge of the tree. Return the minimum time in seconds you have to spend to collect all apples in the tree, starting at vertex 0 and coming back to this vertex.
The edges of the undirected tree are given in the array edges
, where edges[i] = [a<sub>i</sub>, b<sub>i</sub>]
means that exists an edge connecting the vertices a<sub>i</sub>
and b<sub>i</sub>
. Additionally, there is a boolean array hasApple
, where hasApple[i] = true
means that vertex i
has an apple; otherwise, it does not have any apple.
For example:
n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,true,false,true,true,false] Output: 8 Explanation: The figure above represents the given tree where red vertices have an apple. One optimal path to collect all apples is shown by the green arrows.
n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,true,false,false,true,false] Output: 6 Explanation: The figure above represents the given tree where red vertices have an apple. One optimal path to collect all apples is shown by the green arrows.
n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,false,false,false,false,false] Output: 0
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int minTime(int n, vector<vector<int>>& edges, vector<bool>& hasApple) {
vector<vector<int>> adj(n);
for (auto& edge : edges) {
adj[edge[0]].push_back(edge[1]);
adj[edge[1]].push_back(edge[0]);
}
vector<bool> visited(n, false);
function<int(int)> dfs = [&](int node) {
visited[node] = true;
int cost = 0;
for (int neighbor : adj[node]) {
if (!visited[neighbor]) {
int neighborCost = dfs(neighbor);
if (neighborCost > 0 || hasApple[neighbor]) {
cost += neighborCost + 2;
}
}
}
return cost;
};
return dfs(0);
}
int main() {
int n1 = 7;
vector<vector<int>> edges1 = {{0,1},{0,2},{1,4},{1,5},{2,3},{2,6}};
vector<bool> hasApple1 = {false,false,true,false,true,true,false};
cout << "Example 1: " << minTime(n1, edges1, hasApple1) << endl; // Output: 8
int n2 = 7;
vector<vector<int>> edges2 = {{0,1},{0,2},{1,4},{1,5},{2,3},{2,6}};
vector<bool> hasApple2 = {false,false,true,false,false,true,false};
cout << "Example 2: " << minTime(n2, edges2, hasApple2) << endl; // Output: 6
int n3 = 7;
vector<vector<int>> edges3 = {{0,1},{0,2},{1,4},{1,5},{2,3},{2,6}};
vector<bool> hasApple3 = {false,false,false,false,false,false,false};
cout << "Example 3: " << minTime(n3, edges3, hasApple3) << endl; // Output: 0
return 0;
}
Build Adjacency List:
edges
vector is used to construct an adjacency list adj
representing the tree. Each index in adj
corresponds to a node, and the vector at that index stores the node's neighbors.Depth-First Search (DFS):
visited
vector is used to keep track of visited nodes to avoid cycles.Base Case and Recursive Step:
Starting the DFS:
Returning the Result:
The run-time complexity is O(N), where N is the number of nodes in the tree. This is because the DFS visits each node once, and for each node, it iterates through its neighbors. The adjacency list allows us to find the neighbors of each node in O(1) time on average. Therefore, the overall complexity is proportional to the number of nodes and edges in the tree, which is O(N) since it's a tree.
The space complexity is O(N), where N is the number of nodes in the tree. This is due to the following:
adj
takes O(N) space to store the graph.visited
vector takes O(N) space to keep track of visited nodes.This code provides an efficient solution to collect apples in a tree, considering the time to travel and ensuring all apples are collected while starting and ending at vertex 0.