2467. Most Profitable Path in a Tree

40 阅读1分钟

image.png

image.png

Solution

Time Complexity: O(n) Space: O(N)

  • Build the Tree: Use an graph to represent the undirected tree.

  • DFS: Bob going up, Alice going down

  • Find Bob’s Arrival Times

    • DFS from Bob’s starting node toward node 0.

    • Record, in bobPath[node], the time Bob arrives.

    • Only keep nodes in bobPath that actually lie on Bob’s route to 0.

  • Explore Alice’s Routes

    • DFS from node 0 to every leaf.
  • Compare Alice’s arrival time with Bob’s stored time:

    • Alice arrives earlier → collects full amount.

    • Arrive simultaneously → collects half the amount.

    • Bob arrives earlier → collects nothing.

  • Track the maximum profit at any leaf.

// Result: Return that maximum profit.

class Solution {
    private Map<Integer, List<Integer>> tree;
    private Map<Integer, Integer> bobPath; // <node, time of arrival>
    private boolean[] visited;
    private int maxProfit = Integer.MIN_VALUE;

    public int mostProfitablePath(int[][] edges, int bob, int[] amount) {
        int n = amount.length;

        // build tree
        tree = new HashMap<>();
        for (int i = 0; i < n; i++) {
            tree.put(i, new ArrayList<>());
        }
        for (int[] edge : edges) {
            tree.get(edge[0]).add(edge[1]);
            tree.get(edge[1]).add(edge[0]);
        }

        visited = new boolean[n];

        bobPath = new HashMap<>();
        
        dfsBob(bob, 0);
        
        // reset
        Arrays.fill(visited, false);

        dfsAlice(0, 0, 0, amount);

        return maxProfit;  
    }

    // Why a boolean return value helps
    // signal back up the call stack whether or not the path found node 0.
    // false: It means “We did not reach node 0 from here
    public boolean dfsBob(int node, int step) {
        bobPath.put(node, step);

        // to make sure Bob goes only up towards root
        visited[node] = true;

        // signals Bob found the final path to root
        if (node == 0) {
            return true;
        }

        for (int adj : tree.get(node)) {
            // shouldn't return dfsBob, we want to explore all adj
            if (!visited[adj] && dfsBob(adj, step + 1)) { 
                return true;
            }
        }

        // current path didn't work after explore all adjs
        bobPath.remove(node);

        return false;
    }

    public void dfsAlice(int node, int step, int profit, int[] amount) {
        // to make sure alice goes only down to the leaf
        visited[node] = true;

        // CASE 1: bob didn't visited this node OR bob comes later than alice
        if (!bobPath.containsKey(node) || step < bobPath.get(node)) {
            profit += amount[node]; // get full credit
        } 
        // CASE 2:arrive same time
        else if (step == bobPath.get(node)) {
            profit += amount[node] / 2; 
        }

        // CASE 3: bob visited early than alice: alice profit remain same

        // Alice reached leaf node
        if (tree.get(node).size() == 1 && node != 0) {
            maxProfit = Math.max(profit, maxProfit);
        }

        // explore adjacent nodes
        for (int adj : tree.get(node)) {
            if (!visited[adj]) {
                dfsAlice(adj, step + 1, profit, amount);
            }
        }
    }
}