1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/// `f(i, j)` represents:
/// when starting with `piles[i..=j]`,
/// the one who starts first will get `f(i, j)` more than the other one.
///
/// Note that `0 ≤ i ≤ j < piles.len()` and `f(i, j)` might be negative.
///
/// If `i == j`, `f(i, j) = piles[i]`
///
/// Else,
///
/// ```ignore
/// f(i, j) = max(
///     // the player takes piles[i]
///     piles[i] - f(i + 1, j),
///     // the player takes piles[j]
///     piles[j] - f(i, j - 1),
/// )
/// ```
///
/// The one who starts first will win the game if and only if `f(0, n-1) > 0`
///
/// Now the problem is to get `f(0, n-1)`
pub mod dp {
    use std::cmp::max;

    pub fn solve(piles: &[i32]) -> bool {
        let n = piles.len();
        let mut dp = vec![-1; n * n];
        for i in (0..n).rev() {
            for j in i..n {
                if i == j {
                    dp[i * n + j] = piles[i];
                } else {
                    let f1 = dp[(i + 1) * n + j];
                    let f2 = dp[i * n + j - 1];
                    assert_ne!(f1, -1);
                    assert_ne!(f2, -1);
                    dp[i * n + j] = max(piles[i] - f1, piles[j] - f2);
                }
            }
        }

        dp[n - 1] > 0
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn problem_0877() {
        for solve in [
            //
            super::dp::solve,
        ] {
            for (piles, result) in [
                //
                (&[1][..], true),
                (&[1, 4], true),
                (&[5, 3, 4, 5], true),
                (&[3, 7, 2, 3], true),
                (&[1, 3, 1], false),
            ] {
                let solved = solve(piles);

                assert_eq!(solved, result);
            }
        }
    }
}