summaryrefslogtreecommitdiff
path: root/07/src/part-2.rs
blob: 926b9a18d9fe7e6d33a350b0704e2b5dce1bfae3 (plain)
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
70
71
72
73
74
75
76
77
78
79
80
use std::io::{BufRead};

/*
 *  The corresponding real-relaxed problem attains its cost minimum at
 *  position x when
 * 
 *   x + L(x)/(2N)
 *
 *  is as close to the average of all the initial positions as
 *  possible. Here L is the function defined by
 * 
 *   L(x) = (num of initials < x) - (num of initials > x).
 *
 *  We compute L and the average in linear time, and then the best
 *  *integer* approximation of x in linear time. In order to not have
 *  to thoroughly fuss about the error in the real-relaxed problem, we
 *  then check the two integer neighbors of that integer
 *  approximation.
 * 
 *  Equivalently, to avoid floating point computations, we look at
 *  when 2Nx + L(x) is as close as possible to 2S, where S is the sum
 *  of all initial positions.
 *
 */

fn cost(positions: & [i64], dest: i64) -> i64 {
    positions.into_iter().map(|x| ((x-dest).pow(2) + (x-dest).abs())/2).sum()
}

pub fn main() {
    let mut stdin = std::io::stdin();
    let mut handle = stdin.lock();

    let input: String = {
        let mut buf = String::new();
        handle.read_line(&mut buf).unwrap();
        String::from(buf.trim_end())
    };

    let positions: Vec<i64> = {
        let mut tmp: Vec<i64> = input.split(',').map(|w| w.parse().expect("Malformed input")).collect();
        (&mut tmp).sort();
        tmp
    };
    let n: usize = positions.len();
    let s: i64 = (& positions).into_iter().sum();

    // FIXME: Should build this backwards instead, but whatever.
    let l: Vec<i64> = {
        let mut ret: Vec<i64> = Vec::with_capacity(n);
        let mut pos: i64 = positions[0];
        let mut dups: usize = (& positions[0..]).into_iter().take_while(|p| **p == pos).count();
        ret.extend(std::iter::repeat(-(n as i64) + (dups as i64)).take(dups));
        let mut i: usize = dups;
        while i < (n as usize) {
            pos = positions[i];
            dups = (& positions[i..]).into_iter().take_while(|p| **p == pos).count();
            ret.extend(std::iter::repeat((dups as i64) + ret[i-1]).take(dups));
            i += dups;
        }
        ret
    };
    
    let best_relaxed_position: i64 = {
        let mut ret = positions[0];
        let mut best = i64::MAX;
        for i in 0..n {
            let err = (2*(n as i64)*positions[i] + l[i] - 2*s).abs();
            if err < best {
                ret = positions[i];
                best = err;
            }
        }
        ret
    };
    println!("Best relaxed position: {} (will therefore check {}, {}, {}).", best_relaxed_position, best_relaxed_position-1, best_relaxed_position, best_relaxed_position+1);
    let best_cost = [-1, 0, 1].into_iter().map(|offset| cost(& positions, best_relaxed_position + offset)).min().unwrap();
    println!("Minimal fuel cost: {}", best_cost);

}