Submission #53066906


Source Code Expand

use proconio::input_interactive;
use rand::SeedableRng;
use rand_distr::{Distribution, Normal, WeightedAliasIndex};
use rand_pcg::Pcg64Mcg;
use std::collections::BinaryHeap;
use std::mem::swap;
use std::process::exit;

use num_complex::Complex;
use std::f64::consts::PI;

pub trait ChangeMinMax {
    fn chmin(&mut self, x: Self) -> bool;
    fn chmax(&mut self, x: Self) -> bool;
}

impl<T: PartialOrd> ChangeMinMax for T {
    fn chmin(&mut self, x: T) -> bool {
        *self > x && {
            *self = x;
            true
        }
    }

    fn chmax(&mut self, x: T) -> bool {
        *self < x && {
            *self = x;
            true
        }
    }
}

const TIME_LIMIT: f64 = 1.95;
const AREA_SIZE: f64 = 1e5;
const MAX_ACCEL: f64 = 5e2;
const GOAL_RADIUS: f64 = 1e3;
const MAX_TURNS: usize = 5000;

const NUM_PARTICLES: usize = 2000;
const FAIL_DECAY: f64 = 0.8;
const TOO_NEAR_DIST: f64 = 2.0 * GOAL_RADIUS;
const VELOCITY_COEFFICIENT: f64 = 0.9;

fn main() {
    get_time();
    let input = Input::read_input();
    let mut controller = Controller::new(&input);
    let mut filter = ParticleFilter::new(&input, NUM_PARTICLES);
    for turn in 0..MAX_TURNS {
        let action = controller.decide_action(turn, &filter);
        action.print_action();
        match action.action_type {
            ActionType::Accel => {
                filter.accelerate(&Point::new(action.x as f64, action.y as f64));
            },
            ActionType::Measure => {
                input_interactive! {
                    distance: f64,
                };
                filter.update_from_measure(&Point::new(action.x as f64, action.y as f64), distance);
            },
        }
        let move_result = MoveResult::read_move_result();
        if controller.update_trips(&move_result) {
            break;
        }
        filter.update_from_move(&move_result, turn);
    }
    exit(0);
}

struct Input {
    wind_std: f64,
    measure_std: f64,
    start_point: Point,
    goals: Vec<Point>,
    internal_walls: Vec<Segment>,
    walls: Vec<Segment>,
}

impl Input {
    fn read_input() -> Self {
        input_interactive! {
            num_goals: usize,
            num_walls: usize,
            wind_std: f64,
            measure_std: f64,
            start_point_f: (f64, f64),
            goals_f: [(f64, f64); num_goals],
            internal_walls_f: [(f64, f64, f64, f64); num_walls],
        };
        let start_point = Complex::new(start_point_f.0, start_point_f.1);
        let goals = goals_f.into_iter().map(|(x, y)| Point::new(x, y)).collect();
        let internal_walls: Vec<Segment> = internal_walls_f.into_iter().map(|(x1, y1, x2, y2)| Segment { a: Point::new(x1, y1), b: Point::new(x2, y2) }).collect();
        let surrounding_walls = vec![
            Segment {
                a: Complex::new(-AREA_SIZE, -AREA_SIZE),
                b: Complex::new(-AREA_SIZE, AREA_SIZE)
            },
            Segment {
                a: Complex::new(-AREA_SIZE, AREA_SIZE),
                b: Complex::new(AREA_SIZE, AREA_SIZE),
            },
            Segment {
                a: Complex::new(AREA_SIZE, AREA_SIZE),
                b: Complex::new(AREA_SIZE, -AREA_SIZE),
            },
            Segment {
                a: Complex::new(AREA_SIZE, -AREA_SIZE),
                b: Complex::new(-AREA_SIZE, -AREA_SIZE),
            }
        ];
        let mut walls = internal_walls.clone();
        walls.extend(surrounding_walls);

        Input {
            wind_std,
            measure_std,
            start_point,
            goals,
            internal_walls,
            walls,
        }
    }
}

enum ActionType {
    Accel,
    Measure,
}

struct Action {
    action_type: ActionType,
    x: i32,
    y: i32,
}

impl Action {
    fn print_action(&self) {
        let c = match self.action_type {
            ActionType::Accel => 'A',
            ActionType::Measure => 'S',
        };
        println!("{} {} {}", c, self.x, self.y);
    }
}

struct MoveResult {
    collision: bool,
    visited_goals: Vec<usize>,
}

impl MoveResult {
    fn read_move_result() -> Self {
        input_interactive! {
            collision: i32,
            num_visited_goals: usize,
            visited_goals: [usize; num_visited_goals],
        };
        MoveResult {
            collision: collision == 1,
            visited_goals,
        }
    }
}

struct Trip {
    progress: usize,
    targets: Vec<Point>,
    goal_id: usize,
}

struct Controller {
    progress: usize,
    trips: Vec<Trip>,
    visited: Vec<bool>,
    walls: Vec<Segment>,
    measured_wall_id: usize,
    graph: Graph,
    wind_std: f64,
}

impl Controller {
    fn new(input: &Input) -> Self {
        // make cost matrix
        let graph = Graph::new(input);

        // solve tsp
        let order = solve_tsp(&graph.cost_matrix.into_iter().map(|v| v[0..(input.goals.len() + 1)].to_vec()).collect(), false);
        debug_assert_eq!(order.len(), input.goals.len() + 1);
        debug_assert_eq!(order[0], input.goals.len());

        // make trips
        let mut trips = Vec::with_capacity(input.goals.len());
        for i in 0..input.goals.len() {
            trips.push(Trip {
                progress: 0,
                targets: graph.paths[order[i]][order[i + 1]].clone(),
                goal_id: order[i + 1],
            });
        }
        Controller {
            progress: 0,
            trips,
            visited: vec![false; input.goals.len()],
            walls: input.walls.clone(),
            measured_wall_id: input.walls.len(),
            graph: Graph::new(input),
            wind_std: input.wind_std,
        }
    }

    fn decide_action(&mut self, turn: usize, filter: &ParticleFilter) -> Action {
        debug_assert!(self.progress < self.trips.len() && !self.trips[self.progress].targets.is_empty());
        let average_particle = filter.calc_average();
        // todo!("Exploration vs. Exploitation Tradeoff");
        if turn % 2 == 0 || self.wind_std == 0.0 {
            let (x, y) = trunc_accel(&self.decide_accel(&average_particle));
            Action {
                action_type: ActionType::Accel,
                x,
                y,
            }
        } else {
            self.decide_measure(&average_particle)
        }
    }

    fn decide_accel(&mut self, average_particle: &Particle) -> Point {
        let pos = average_particle.position;
        let vel = average_particle.velocity;
        let trip = &mut self.trips[self.progress];
        while trip.progress + 1 < trip.targets.len() {
            if (pos - trip.targets[trip.progress]).norm() < GOAL_RADIUS {
                trip.progress += 1;
            } else if !can_move_linearly(&self.walls, &pos, &trip.targets[trip.progress + 1]) {
                break;
            } else if calc_wall_distance(&self.walls, &Segment { a: pos, b: trip.targets[trip.progress + 1], }) > TOO_NEAR_DIST {
                trip.progress += 1;
            } else {
                break;
            }
        }
        if !can_move_linearly(&self.walls, &pos, &trip.targets[trip.progress]) {
            trip.targets = self.graph.calc_path(&self.walls, trip.goal_id, &pos);
            trip.progress = 0;
        }
        let goal = trip.targets[trip.progress] - average_particle.position;
        let path = Segment {
            a: Point::new(0.0, 0.0),
            b: goal,
        };
        let acc = projection(&path, &vel);
        let approaching = dot(&goal, &acc) > 0.0;
        let fix = acc - vel;
        if fix.norm() >= MAX_ACCEL {
            Point::from_polar(MAX_ACCEL, fix.arg())
        } else {
            let max_proceed = (MAX_ACCEL * MAX_ACCEL - fix.norm_sqr()).sqrt();
            let proceed = (self.max_velocity(goal.norm()) + if approaching { -acc.norm() } else { acc.norm() }).clamp(-max_proceed, max_proceed);
            fix + Point::from_polar(proceed, goal.arg())
        }
    }

    fn decide_measure(&mut self, average_particle: &Particle) -> Action {
        let p = average_particle.position;
        let mut min_distance = f64::INFINITY;
        let mut measuring_wall_id = self.walls.len();
        let mut ret = Action {
            action_type: ActionType::Measure,
            x: 0,
            y: 0,
        };
        for wall_id in 0..self.walls.len() {
            if wall_id == self.measured_wall_id {
                continue;
            }
            let q = calc_nearest_point(&self.walls[wall_id], &p);
            if can_move_linearly(&self.walls, &p, &q) {
                if min_distance.chmin((q - p).norm()) {
                    measuring_wall_id = wall_id;
                    (ret.x, ret.y) = trunc_accel(&Point::from_polar(MAX_ACCEL, (q - p).arg()));
                }
            }
        }
        debug_assert!(ret.x != 0 || ret.y != 0);
        self.measured_wall_id = measuring_wall_id;
        ret
    }

    fn update_trips(&mut self, move_result: &MoveResult) -> bool {
        debug_assert!(self.progress < self.trips.len() && !self.trips[self.progress].targets.is_empty());
        for goal_id in move_result.visited_goals.iter() {
            debug_assert!(!self.visited[*goal_id]);
            self.visited[*goal_id] = true;
        }
        while self.progress < self.trips.len() {
            if self.visited[self.trips[self.progress].goal_id] {
                self.progress += 1;
            } else {
                break;
            }
        }
        self.progress == self.trips.len()
    }

    fn max_velocity(&self, distance: f64) -> f64 {
        // Theoretically, we can accelerate up to arround (2.0 * a * d).sqrt() where a is the acceleration and d is the distance.
        if self.wind_std == 0.0 {
            VELOCITY_COEFFICIENT * (2.0 * MAX_ACCEL * distance).sqrt()
        } else {
            VELOCITY_COEFFICIENT * (MAX_ACCEL * distance).sqrt()
        }
    }
}

struct Graph {
    points: Vec<Point>,
    cost_matrix: Vec<Vec<i64>>,
    paths: Vec<Vec<Vec<Point>>>,
}

impl Graph {
    fn new(input: &Input) -> Self {
        // It returns the tuple of the cost matrix and its path.
        let mut points = input.goals.clone();
        points.push(input.start_point);
        points.extend(make_waypoints(input));

        let mut edges = vec![Vec::new(); points.len()];
        for u in 0..points.len() {
            for v in 0..points.len() {
                if can_move_linearly(&input.walls, &points[u], &points[v]) {
                    edges[u].push((v, calc_edge_weight(&input.walls, &points[u], &points[v]) as i64));
                }
            }
        }
        let n = input.goals.len() + 1;
        let inf = i64::max_value() / 2;
        let mut cost_matrix = vec![vec![0; points.len()]; n];
        let mut paths = vec![vec![Vec::new(); points.len()]; n];
        let mut costs = vec![0; points.len()];
        let mut from = vec![0; points.len()];
        for s in 0..n {
            // Dijkstra's algorithm
            costs.fill(-inf);
            costs[s] = 0;
            from.fill(points.len());
            let mut todo = BinaryHeap::new();
            todo.push((0, s));
            while !todo.is_empty() {
                let (c, u) = todo.pop().unwrap();
                if c < costs[u] {
                    continue;
                }
                for (v, w) in edges[u].iter() {
                    let cost = c - w;
                    if costs[*v].chmax(cost) {
                        from[*v] = u;
                        todo.push((cost, *v));
                    }
                }
            }
            // restore paths
            for t in 0..points.len() {
                debug_assert_ne!(costs[t], -inf);
                cost_matrix[s][t] = -costs[t];
                let mut v = t;
                while v != s {
                    paths[s][t].push(points[v]);
                    v = from[v];
                }
                paths[s][t].reverse();
            }
        }
        Graph {
            points,
            cost_matrix,
            paths,
        }
    }

    fn calc_path(&self, walls: &Vec<Segment>, goal_id: usize, p: &Point) -> Vec<Point> {
        let mut min_cost = i64::max_value() / 2;
        let mut min_cost_v = self.points.len();
        for v in 0..self.points.len() {
            if !can_move_linearly(walls, p, &self.points[v]) {
                continue;
            }
            if min_cost.chmin(self.cost_matrix[goal_id][v] + calc_edge_weight(walls, p, &self.points[v])) {
                min_cost_v = v;
            }
        }
        debug_assert_ne!(min_cost_v, self.points.len());
        let mut ret = self.paths[goal_id][min_cost_v].clone();
        ret.reverse();
        ret.push(self.points[goal_id]);
        ret
    }
}

fn calc_edge_weight(walls: &Vec<Segment>, p: &Point, q: &Point) -> i64 {
    let mut weight = (p - q).norm().sqrt();
    let wall_distance = calc_wall_distance(walls, &Segment { a: *p, b: *q, });
    if wall_distance < TOO_NEAR_DIST {
        // todo!("adjust hyper parameter");
        weight *= 1.0 + 0.5 * (TOO_NEAR_DIST / wall_distance.max(1.0)).log2();
    }
    weight as i64
}

fn make_waypoints(input: &Input) -> Vec<Point> {
    let mut waypoints = Vec::new();
    for i in 0..(input.internal_walls.len() + input.goals.len() + 1) {
        let points = 
            if i < input.internal_walls.len() {
                vec![input.internal_walls[i].a, input.internal_walls[i].b]
            } else if i - input.internal_walls.len() < input.goals.len() {
                vec![input.goals[i - input.internal_walls.len()]]
            } else {
                vec![input.start_point]
            };
        for p in points {
            if p.re.abs() == AREA_SIZE || p.im.abs() == AREA_SIZE {
                continue;
            }
            for wall in input.walls.iter() {
                if p == wall.a || p == wall.b {
                    continue;
                }
                let q = calc_nearest_point(wall, &p);
                if can_move_linearly(&input.walls, &p, &q) {
                    waypoints.push((p + q).scale(0.5));
                }
            }
        }
    }
    waypoints
}

fn trunc_accel(accel: &Point) -> (i32, i32) {
    let x = accel.re.trunc() as i32;
    let y = accel.im.trunc() as i32;
    debug_assert!(x * x + y * y <= MAX_ACCEL as i32 * MAX_ACCEL as i32);
    (x, y)
}

#[derive(Clone)]
struct Particle {
    position: Point,
    velocity: Point,
    likelihood: f64,
}

struct ParticleFilter {
    rng: Pcg64Mcg,
    num_particles: usize,
    particles: Vec<Particle>,
    particles_buffer: Vec<Particle>,
    measure_std: f64,
    goals: Vec<Point>,
    walls: Vec<Segment>,
    wind_norm: Normal<f64>,
    visited: Vec<bool>,
}

impl ParticleFilter {
    fn new(input: &Input, num_particles: usize) -> Self {
        let particle = Particle {
            position: input.start_point,
            velocity: Complex::new(0.0, 0.0),
            likelihood: 1.0 / num_particles as f64,
        };
        ParticleFilter {
            rng: Pcg64Mcg::seed_from_u64(0),
            num_particles,
            particles: vec![particle; num_particles],
            particles_buffer: Vec::with_capacity(num_particles),
            measure_std: input.measure_std,
            goals: input.goals.clone(),
            walls: input.walls.clone(),
            wind_norm: Normal::<f64>::new(0.0, input.wind_std).unwrap(),
            visited: vec![false; input.goals.len()],
        }
    }

    fn update_from_measure(&mut self, direction: &Point, measured_distance: f64) {
        let mut sum_likelihood = 0.0;
        for particle_id in 0..self.particles.len() {
            self.particles[particle_id].likelihood *= self.measure_likelihood(&self.particles[particle_id].position, direction, measured_distance);
            sum_likelihood += self.particles[particle_id].likelihood;
        }
        for particle in &mut self.particles {
            particle.likelihood /= sum_likelihood;
        }
    }

    fn measure_likelihood(&self, position: &Point, direction: &Point, measured_distance: f64) -> f64 {
        let ray = get_ray(position, direction);
        let collision_point = calc_collision_point(&self.walls, &ray);
        debug_assert_ne!(collision_point, ray.b);
        let distance = (collision_point - position).norm().max(EPS);
        self.measure_norm_pdf(measured_distance / distance).max(EPS)
    }

    fn measure_norm_pdf(&self, x: f64) -> f64 {
        // You can ignore the constant multiplication because we will normalize it later.
        let y = (x - 1.0) / (self.measure_std + EPS);
        (-0.5 * y * y).exp()
    }

    fn accelerate(&mut self, accel: &Point) {
        for particle in &mut self.particles {
            particle.velocity += accel;
        }
    }

    fn update_from_move(&mut self, move_result: &MoveResult, turn: usize) {
        self.particles_buffer.clear();
        while self.particles_buffer.len() < self.num_particles {
            let weights: Vec<f64> = self.particles.iter().map(|particle| particle.likelihood).collect();
            let distribution = WeightedAliasIndex::new(weights).unwrap();
            for _ in 0..self.num_particles {
                let particle_id = distribution.sample(&mut self.rng);
                let mut particle = self.particles[particle_id].clone();
                particle.velocity.re += self.wind_norm.sample(&mut self.rng);
                particle.velocity.im += self.wind_norm.sample(&mut self.rng);
                if self.consistent_with_move_result(&mut particle, move_result) {
                    self.particles_buffer.push(particle);
                    if self.particles_buffer.len() == self.num_particles {
                        break;
                    }
                } else {
                    self.particles[particle_id].likelihood *= FAIL_DECAY;
                }
            }
            if get_time() > (turn as f64 / MAX_TURNS as f64) * TIME_LIMIT {
                if !self.particles_buffer.is_empty() {
                    break;
                } else if !move_result.visited_goals.is_empty() {
                    let goal = self.goals[move_result.visited_goals[0]];
                    for particle in &mut self.particles {
                        particle.position = goal;
                        self.particles_buffer.push(particle.clone());
                    }
                    break;
                } else if move_result.collision {
                    for particle in &mut self.particles {
                        particle.velocity = Point::new(0.0, 0.0);
                        self.particles_buffer.push(particle.clone());
                    }
                    break;
                } else {
                    // All particles died :(
                    return;
                }
            }
        }
        swap(&mut self.particles, &mut self.particles_buffer);
        let likelihood = 1.0 / self.particles.len() as f64;
        for particle in &mut self.particles {
            particle.likelihood = likelihood;
        }
        for goal_id in move_result.visited_goals.iter() {
            self.visited[*goal_id] = true;
        }
    }

    fn consistent_with_move_result(&self, particle: &mut Particle, move_result: &MoveResult) -> bool {
        let path = Segment {
            a: particle.position,
            b: particle.position + particle.velocity,
        };
        if path.b.re.abs() < AREA_SIZE && path.b.im.abs() < AREA_SIZE && can_move_linearly(&self.walls, &path.a, &path.b) {
            particle.position = path.b;
            !move_result.collision && self.calc_visited_goals(&path) == move_result.visited_goals
        } else {
            particle.velocity = Point::new(0.0, 0.0);
            move_result.collision
        }
    }

    fn calc_visited_goals(&self, path: &Segment) -> Vec<usize> {
        let mut ret = Vec::new();
        for goal_id in 0..self.goals.len() {
            if !self.visited[goal_id] && calc_distance(path, &self.goals[goal_id]) <= GOAL_RADIUS {
                ret.push(goal_id);
            }
        }
        ret
    }

    fn calc_average(&self) -> Particle {
        let mut position = Point::new(0.0, 0.0);
        let mut velocity = Point::new(0.0, 0.0);
        for particle in self.particles.iter() {
            position += particle.likelihood * particle.position;
            velocity += particle.likelihood * particle.velocity;
        }
        Particle {
            position,
            velocity,
            likelihood: 1.0,
        }
    }
}

fn can_move_linearly(walls: &Vec<Segment>, p: &Point, q: &Point) -> bool {
    let path = Segment {
        a: p + Point::from_polar(1.0, (q - p).arg()),
        b: q - Point::from_polar(1.0, (q - p).arg()),
    };
    for wall in walls {
        if intersect(&path, wall) {
            return false;
        }
    }
    true
}

fn calc_wall_distance(walls: &Vec<Segment>, path: &Segment) -> f64 {
    let mut ret = f64::INFINITY;
    for wall in walls {
        ret.chmin(calc_distance(wall, &path.a));
        ret.chmin(calc_distance(wall, &path.b));
        ret.chmin(calc_distance(path, &wall.a));
        ret.chmin(calc_distance(path, &wall.b));
    }
    ret
}

fn calc_collision_point(walls: &Vec<Segment>, path: &Segment) -> Point {
    let mut max_distance = (path.a - path.b).norm();
    let mut ret = path.b;
    for segment in walls {
        if intersect(segment, path) {
            let collision_point = cross_point(segment, path);
            let distance = (collision_point - path.a).norm();
            if max_distance.chmin(distance) {
                ret = collision_point;
            }
        }
    }
    ret
}

fn get_ray(position: &Point, direction: &Point) -> Segment {
    Segment {
        a: *position,
        b: position + direction.scale(4.0 * AREA_SIZE / direction.norm()),
    }
}

// ------------------
// geometry library

pub type Point = Complex<f64>;

pub const EPS: f64 = 1e-9;

pub fn radian_to_degree(theta: f64) -> f64 {
    (180.0 / PI) * theta
}

pub fn degree_to_radian(degree: f64) -> f64 {
    (PI / 180.0) * degree
}

// almost equal
pub fn eq(a: f64, b: f64) -> bool {
    (b - a).abs() < EPS
}

// angle of b-a-c
pub fn angle(a: &Point, b: &Point, c: &Point) -> f64 {
    (c - a).arg() - (b - a).arg()
}

pub fn rot(p: &Point, theta: f64) -> Point {
    Point::from_polar(1.0, theta) * p
}

pub fn cross_product(a: &Point, b: &Point) -> f64 {
    a.re * b.im - a.im * b.re
}

pub fn dot(a: &Point, b: &Point) -> f64 {
    a.re * b.re + a.im * b.im
}

// https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_C
// positional relationship between b and c from a
pub fn ccw(a: &Point, b: &Point, c: &Point) -> i32 {
    let ba = b - a;
    let ca = c - a;
    if cross_product(&ba, &ca) > EPS {
        1 // counter-clockwise
    } else if cross_product(&ba, &ca) < -EPS {
        -1 // clockwise
    } else if dot(&ba, &ca) < 0.0 {
        2 // online back (c-a-b)
    } else if ba.norm_sqr() < ca.norm_sqr() {
        -2 // online front (a-b-c)
    } else {
        0 // on segment (a-c-b)
    }
}

#[derive(Clone, PartialEq)]
pub struct Segment {
    a: Point,
    b: Point,
}

pub fn projection(s: &Segment, p: &Point) -> Point {
    let t = dot(&(p - s.a), &(s.b - s.a)) / (s.b - s.a).norm_sqr();
    s.a + (s.b - s.a).scale(t)
}

pub fn on_segment(s: &Segment, p: &Point) -> bool {
    ccw(&s.a, &s.b, p) == 0
}

pub fn intersect(s: &Segment, t: &Segment) -> bool {
    ccw(&s.a, &s.b, &t.a) * ccw(&s.a, &s.b, &t.b) <= 0 && ccw(&t.a, &t.b, &s.a) * ccw(&t.a, &t.b, &s.b) <= 0
}

pub fn calc_distance(s: &Segment, p: &Point) -> f64 {
    let r = projection(s, p);
    if dot(&(s.a - r), &(s.b - r)) < 0.0 {
        (r - p).norm()
    } else {
        (s.a - p).norm().min((s.b - p).norm())
    }
}

pub fn calc_nearest_point(s: &Segment, p: &Point) -> Point {
    let r = projection(s, p);
    if dot(&(s.a - r), &(s.b - r)) < 0.0 {
        r
    } else if (s.a - p).norm_sqr() < (s.b - p).norm_sqr() {
        s.a
    } else {
        s.b
    }
}

pub fn cross_point(s: &Segment, t: &Segment) -> Point {
    let a = cross_product(&(s.b - s.a), &(t.b - t.a));
    let b = cross_product(&(s.b - s.a), &(s.b - t.a));
    if eq(a.abs(), 0.0) && eq(b.abs(), 0.0) {
        t.a
    } else {
        t.a + (b / a) * (t.b - t.a)
    }
}

// ------------------

// calculate the TSP optimal solution
// start from the last vertex and end to the last vertex
pub fn solve_tsp(cost_matrix: &Vec<Vec<i64>>, back_to_the_start_point: bool) -> Vec<usize> {
    if cost_matrix.len() == 1 {
        return vec![0];
    }
    let n = cost_matrix.len() - 1;
    let inf = i64::max_value() / 2;

    // dp
    let mut dp = vec![vec![inf; 1 << n]; n];
    for v in 0..n {
        dp[v][1 << v] = cost_matrix[n][v];
    }
    for s in 1..(1 << n) {
        for u in 0..n {
            if ((s >> u) & 1) == 0 {
                continue;
            }
            for v in 0..n {
                if ((s >> v) & 1) == 0 {
                    let cost = dp[u][s] + cost_matrix[u][v];
                    dp[v][s | (1 << v)].chmin(cost);
                }
            }
        }
    }
    let mut last = n;
    let mut cost = inf;
    let mut s = (1 << n) - 1;
    for v in 0..n {
        if back_to_the_start_point {
            if cost.chmin(dp[v][s] + cost_matrix[v][n]) {
                last = v;
            }
        } else {
            if cost.chmin(dp[v][s]) {
                last = v;
            }
        }
    }
    let mut ret = vec![n; n + 1 + back_to_the_start_point as usize];
    ret[n] = last;
    s ^= 1 << last;
    for i in (1..n).rev() {
        for v in 0..n {
            if (s & (1 << v)) > 0 && dp[v][s] + cost_matrix[v][last] == dp[last][s | (1 << last)] {
                ret[i] = v;
                s ^= 1 << v;
                break;
            }
        }
        debug_assert_ne!(ret[i], n);
    }
    ret
}

pub fn get_time() -> f64 {
    static mut STIME: f64 = -1.0;
    let t = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap();
    let ms = t.as_secs() as f64 + t.subsec_nanos() as f64 * 1e-9;
    unsafe {
        if STIME < 0.0 {
            STIME = ms;
        }
        ms - STIME
    }
}

Submission Info

Submission Time
Task B - Windy Drone Control (B)
User eijirou
Language Rust (rustc 1.70.0)
Score 563144
Code Size 27079 Byte
Status AC
Exec Time 419 ms
Memory 4128 KiB

Judge Result

Set Name test_ALL
Score / Max Score 563144 / 600000
Status
AC × 60
Set Name Test Cases
test_ALL test_0000.txt, test_0001.txt, test_0002.txt, test_0003.txt, test_0004.txt, test_0005.txt, test_0006.txt, test_0007.txt, test_0008.txt, test_0009.txt, test_0010.txt, test_0011.txt, test_0012.txt, test_0013.txt, test_0014.txt, test_0015.txt, test_0016.txt, test_0017.txt, test_0018.txt, test_0019.txt, test_0020.txt, test_0021.txt, test_0022.txt, test_0023.txt, test_0024.txt, test_0025.txt, test_0026.txt, test_0027.txt, test_0028.txt, test_0029.txt, test_0030.txt, test_0031.txt, test_0032.txt, test_0033.txt, test_0034.txt, test_0035.txt, test_0036.txt, test_0037.txt, test_0038.txt, test_0039.txt, test_0040.txt, test_0041.txt, test_0042.txt, test_0043.txt, test_0044.txt, test_0045.txt, test_0046.txt, test_0047.txt, test_0048.txt, test_0049.txt, test_0050.txt, test_0051.txt, test_0052.txt, test_0053.txt, test_0054.txt, test_0055.txt, test_0056.txt, test_0057.txt, test_0058.txt, test_0059.txt
Case Name Status Exec Time Memory
test_0000.txt AC 351 ms 3592 KiB
test_0001.txt AC 225 ms 3168 KiB
test_0002.txt AC 292 ms 3684 KiB
test_0003.txt AC 182 ms 3496 KiB
test_0004.txt AC 328 ms 3380 KiB
test_0005.txt AC 341 ms 3808 KiB
test_0006.txt AC 230 ms 3700 KiB
test_0007.txt AC 347 ms 3324 KiB
test_0008.txt AC 245 ms 3648 KiB
test_0009.txt AC 257 ms 3608 KiB
test_0010.txt AC 191 ms 3596 KiB
test_0011.txt AC 366 ms 3492 KiB
test_0012.txt AC 225 ms 3516 KiB
test_0013.txt AC 241 ms 3292 KiB
test_0014.txt AC 209 ms 3304 KiB
test_0015.txt AC 212 ms 3472 KiB
test_0016.txt AC 209 ms 3724 KiB
test_0017.txt AC 286 ms 3324 KiB
test_0018.txt AC 347 ms 4128 KiB
test_0019.txt AC 219 ms 3528 KiB
test_0020.txt AC 356 ms 3880 KiB
test_0021.txt AC 320 ms 3584 KiB
test_0022.txt AC 209 ms 3536 KiB
test_0023.txt AC 228 ms 3544 KiB
test_0024.txt AC 239 ms 3632 KiB
test_0025.txt AC 269 ms 3400 KiB
test_0026.txt AC 249 ms 3372 KiB
test_0027.txt AC 259 ms 3376 KiB
test_0028.txt AC 176 ms 3604 KiB
test_0029.txt AC 183 ms 3612 KiB
test_0030.txt AC 241 ms 3508 KiB
test_0031.txt AC 362 ms 3324 KiB
test_0032.txt AC 303 ms 3492 KiB
test_0033.txt AC 292 ms 3504 KiB
test_0034.txt AC 269 ms 3540 KiB
test_0035.txt AC 313 ms 3560 KiB
test_0036.txt AC 262 ms 3372 KiB
test_0037.txt AC 339 ms 3424 KiB
test_0038.txt AC 309 ms 3428 KiB
test_0039.txt AC 247 ms 3892 KiB
test_0040.txt AC 345 ms 3416 KiB
test_0041.txt AC 333 ms 3664 KiB
test_0042.txt AC 188 ms 3524 KiB
test_0043.txt AC 363 ms 3760 KiB
test_0044.txt AC 184 ms 3388 KiB
test_0045.txt AC 225 ms 3616 KiB
test_0046.txt AC 194 ms 3448 KiB
test_0047.txt AC 321 ms 3568 KiB
test_0048.txt AC 167 ms 3380 KiB
test_0049.txt AC 364 ms 3320 KiB
test_0050.txt AC 196 ms 3636 KiB
test_0051.txt AC 276 ms 3920 KiB
test_0052.txt AC 219 ms 3396 KiB
test_0053.txt AC 216 ms 3576 KiB
test_0054.txt AC 277 ms 3448 KiB
test_0055.txt AC 259 ms 3556 KiB
test_0056.txt AC 217 ms 3292 KiB
test_0057.txt AC 230 ms 3108 KiB
test_0058.txt AC 304 ms 3368 KiB
test_0059.txt AC 419 ms 3544 KiB