문제 링크

기하학! 나는 기하학이 싫어

문제 설명

  1. 좌표 평면에 두 터렛이 있고 좌표, 목표물의 거리를 알려준다 (x1, y1, r1, x2, y2, r2)
  2. 목표물이 위치할 수 있는 점의 개수를 찾으면 끝

접근

두 원의 교점의 개수를 생각하자.

두 원 중심 사이의 거리와, 두 반지름의 관계를 생각하면 해결된다. (많은 원을 그려서 파악…)

혹시 두 점 사이의 거리를 구하면 제곱은으로 인해 문제가 발생할 수 있으니 f64를 적용하긴 했는데.. (제곱하면 됐음)

경우는

  1. 완전 일치
  2. 멀리 떨어진 경우
  3. 내부에 하나가 그냥 포함된 경우
  4. 외부에서 만나는 경우
  5. 내부에서 한쪽으로 만나는 경우
  6. 두 점에서 만나는 경우
번호 교점 조건
1 -1 거리 = 0, 반지름 같음
2 0 거리 > 두 반지름 합
3 0 거리가 반지름의 차보다 작음
4 1 거리 = 반지름 합
5 1 거리 = 반지름 차
6 2 위의 모든 조건을 통과한 경우

if 문으로 작성하기에 순서가 중요하다.

중요한 값들

  • 거리 d^2 = (x1 - x2)^2 + (y1 - y2)^2
  • 반지름의 합 제곱
  • 반지름의 차 제곱

정답 코드

//! ## 백준 1002 (실버3) 터렛
//!
//! x1,y1,r1,x2,y2,r2
//!
//! [문제 링크](https://www.acmicpc.net/problem/1002)

use std::io::{self, BufWriter, Read, Write};

pub fn main() {
    let mut input = String::new();
    io::stdin()
        .read_to_string(&mut input)
        .expect("expected input");

    let mut lines = input.lines();
    let mut output = BufWriter::new(io::stdout().lock());

    let t: usize = lines
        .next()
        .expect("expect T")
        .trim()
        .parse()
        .expect("can not parse");

    for _ in 0..t {
        let info: Vec<isize> = lines
            .next()
            .unwrap()
            .split_ascii_whitespace()
            .map(|s| s.parse().unwrap())
            .collect();

        let x1 = info[0] as f64;
        let y1 = info[1] as f64;
        let r1 = info[2] as f64;

        let x2 = info[3] as f64;
        let y2 = info[4] as f64;
        let r2 = info[5] as f64;

        let res = get_location_cnt(x1, y1, r1, x2, y2, r2);
        writeln!(output, "{}", res).expect("write failed");
    }
}

fn get_location_cnt(x1: f64, y1: f64, r1: f64, x2: f64, y2: f64, r2: f64) -> isize {
    // 거리 계산
    let dx = x1 - x2;
    let dy = y1 - y2;
    let d_sq = dx * dx + dy * dy;

    // 반지름 합 / 차 제곱
    let r_sum_sq = (r1 + r2) * (r1 + r2);
    let r_diff_sq = (r1 - r2) * (r1 - r2);

    // 완전 일치
    if d_sq < 1e-9 && r1 == r2 {
        return -1;
    }

    // 멂
    if d_sq > r_sum_sq {
        return 0;
    }

    // 내부에 있고 만나진 않음
    if d_sq < r_diff_sq {
        return 0;
    }

    // 내접 (1개)
    if (d_sq - r_sum_sq).abs() < 1e-9 {
        return 1;
    }
    // 외접
    if (d_sq - r_diff_sq).abs() < 1e-9 {
        return 1;
    }
    // 두 점 교차
    2
}

마무리하며

아… 조건문 순서를 생각하면서 코드를 치려니 쉽지 않았다

그래도 간만에 좌표평면과 원을 직접 그려가면서 이것저것 생각해본 것은 신선한 자극이 아니었을까…

f64 안써도 됐을 것 같고.

뭐 어쨌든 해냈다!

화이팅화이팅

-뱅-