главная Π½ΠΎΠ²ΠΎΠ΅ Π»ΡƒΡ‡ΡˆΠ΅Π΅ Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ

Бсылка Π½Π° Π·Π°Π΄Π°Ρ‡Ρƒ β€” 2467. Most Profitable Path in a Tree.

πŸ“Œ ОписаниС Π·Π°Π΄Π°Ρ‡ΠΈ

Π”Π°Π½ΠΎ Π½Π΅ΠΎΡ€ΠΈΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½Π½ΠΎΠ΅ ΠΊΠΎΡ€Π½Π΅Π²ΠΎΠ΅ Π΄Π΅Ρ€Π΅Π²ΠΎ с n ΡƒΠ·Π»Π°ΠΌΠΈ (Π½ΡƒΠΌΠ΅Ρ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΌΠΈ ΠΎΡ‚ 0 Π΄ΠΎ n-1).

НуТно Π½Π°ΠΉΡ‚ΠΈ ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ чистый Π΄ΠΎΡ…ΠΎΠ΄ Алисы ΠΏΡ€ΠΈ ΠΎΠΏΡ‚ΠΈΠΌΠ°Π»ΡŒΠ½ΠΎΠΌ Π²Ρ‹Π±ΠΎΡ€Π΅ ΠΏΡƒΡ‚ΠΈ.

πŸ’­ ИдСя

ВмСсто Ρ€Π°Π·Π΄Π΅Π»ΡŒΠ½ΠΎΠ³ΠΎ запуска BFS для поиска ΠΏΡƒΡ‚ΠΈ Π‘ΠΎΠ±Π° ΠΈ ΠΏΠΎΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅Π³ΠΎ ΠΏΡ€ΠΎΡ…ΠΎΠ΄Π° динамичСского программирования ΠΏΠΎ ΡƒΠ·Π»Π°ΠΌ Π΄Π΅Ρ€Π΅Π²Π°, ΠΌΡ‹ Ρ€Π΅ΡˆΠΈΠΌ Π·Π°Π΄Π°Ρ‡Ρƒ ΠΎΠ΄Π½ΠΈΠΌ рСкурсивным DFS-ΠΏΡ€ΠΎΡ…ΠΎΠ΄ΠΎΠΌ.

Для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΡƒΠ·Π»Π° Π±ΡƒΠ΄Π΅ΠΌ Π²Ρ‹Ρ‡ΠΈΡΠ»ΡΡ‚ΡŒ:

πŸ’‘ Π’ ΠΈΡ‚ΠΎΠ³Π΅ ΠΎΠ΄Π½ΠΈΠΌ DFS вычислим ΠΎΠΏΡ‚ΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΏΡƒΡ‚ΡŒ Алисы ΠΈ ΡƒΡ‡Ρ‚Ρ‘ΠΌ влияниС Π‘ΠΎΠ±Π°. πŸš€

πŸ”Ž ΠŸΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎΡΡ‚ΠΈ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π°

  1. Π‘Ρ‚Ρ€ΠΎΠΈΠΌ Π΄Π΅Ρ€Π΅Π²ΠΎ Π² Π²ΠΈΠ΄Π΅ списка смСТности (Vec<Vec<usize>>).
  2. ЗапускаСм рСкурсивный DFS:
    • Если ΡƒΠ·Π΅Π» – лист, Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ amount[node] (ΠΈΠ»ΠΈ 0, Ссли Ρ‚Π°ΠΌ Π‘ΠΎΠ±).
    • РСкурсивно запускаСмся ΠΈΠ· сосСдСй (ΠΈΡΠΊΠ»ΡŽΡ‡Π°Ρ родитСля).
    • Находим расстояниС Π½Π° ΠΏΡƒΡ‚ΠΈ ΠΎΡ‚ Π‘ΠΎΠ±Π°.
    • Π’Ρ‹Π±ΠΈΡ€Π°Π΅ΠΌ Π½Π°ΠΈΠ»ΡƒΡ‡ΡˆΠΈΠΉ Π΄ΠΎΡ…ΠΎΠ΄ Алисы.
    • ΠšΠΎΡ€Ρ€Π΅ΠΊΡ‚ΠΈΡ€ΡƒΠ΅ΠΌ Π΄ΠΎΡ…ΠΎΠ΄ Π² зависимости ΠΎΡ‚ рассчитанного Π²Ρ€Π΅ΠΌΠ½ΠΈ Π‘ΠΎΠ±Π°.

⏳ Асимптотика

πŸ’» Π˜ΡΡ…ΠΎΠ΄Π½Ρ‹ΠΉ ΠΊΠΎΠ΄

use std::cmp::Ordering::{Less, Equal, Greater};

struct ProblemContext<'a> {
    bob: usize,
    amount: &'a [i32],
    tree: &'a [Vec<usize>],
}

impl Solution {
    pub fn most_profitable_path(edges: Vec<Vec<i32>>, bob: i32, amount: Vec<i32>) -> i32 {
        let n = amount.len();
        let mut tree = vec![vec![]; n];

        // Construct adjacency list
        for edge in &edges {
            let (u, v) = (edge[0] as usize, edge[1] as usize);
            tree[u].push(v);
            tree[v].push(u);
        }

        let ctx = ProblemContext {
            bob: bob as usize,
            amount: &amount,
            tree: &tree,
        };

        // Start DFS from the root (0), parent set as usize::MAX (invalid parent)
        let (alice_profit, _) = Self::dp_rec(0, usize::MAX, 0, &ctx);
        alice_profit
    }

    // Recursive DFS to compute Alice's max profit and Bob's distance from `node`
    fn dp_rec(node: usize, parent: usize, current_depth: usize, ctx: &ProblemContext) -> (i32, usize) {
        let bob_init_depth = if node == ctx.bob { 0 } else { ctx.tree.len() };

        // If `node` is a leaf (not the root), handle Alice's profit correctly
        if ctx.tree[node].len() == 1 && current_depth != 0 {
            let alice_profit = if node == ctx.bob { 0 } else { ctx.amount[node] };
            return (alice_profit, bob_init_depth);
        }

        // Explore child nodes recursively and find:
        //  - Maximum profit Alice can collect (`alice_profit`)
        //  - Minimum distance Bob needs to reach this node (`bob_distance`)
        let (alice_profit, bob_distance) = ctx.tree[node]
            .iter()
            .filter(|&&neighbor| neighbor != parent) // Avoid backtracking to parent
            .map(|&neighbor| Self::dp_rec(neighbor, node, current_depth + 1, ctx))
            .fold((i32::MIN, bob_init_depth), |(a_profit, b_dist), (a, b)| {
                (a_profit.max(a), b_dist.min(b + 1))
            });

        // Adjust Alice's profit based on when Bob arrives at this node
        let adjusted_profit = match current_depth.cmp(&bob_distance) {
            Less => alice_profit + ctx.amount[node],  // Alice arrives first β†’ full amount
            Equal => alice_profit + ctx.amount[node] / 2, // Alice & Bob arrive together β†’ split amount
            Greater => alice_profit, // Bob arrives first β†’ Alice gets nothing
        };

        (adjusted_profit, bob_distance)
    }
}

Tags: #rust #algorithms #tree #dp #dfs