알고리즘

[programmers] 교점에 별 만들기

졔졔311 2023. 6. 17. 23:12
728x90
반응형

https://school.programmers.co.kr/learn/courses/30/lessons/87377

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

---------------------------------------------------핵심 알고리즘--------------------------------------------

 

math

 

---------------------------------------------------풀이----------------------------------------------------

 

문제 아래쪽에 나온 공식을 그대로 풀면 된다.

Ax + By + E = 0
Cx + Dy + F = 0
x = (BF-ED)/(AD-BC), y = (EC-AF)/(AD-BC)
(AD-BC == 0 이면 두 직선은 평행 또는 일치)

line에서 i와 j 2개씩을 선택해서(i는 0부터 n까지, j는 i+1부터 n까지)

분모인 AD-BC를 구하고, 이 값이 0이면 패스한다.

아니면 위 공식에 따라 x와 y를 구하고 그 값이 정수이면 set에 추가한다.

set에 추가된 값들 중에서 x와 y의 최소 최대 범위를 구하고,

그 범위를 돌면서 해당 정수 좌표 값이 set에 존재하면 *을 출력, 아니면 .을 출력한다.

#include <string>
#include <vector>
#include <set>
#include <cstdio>
#include <iostream>
#include <limits.h>

using namespace std;

vector<string> solution(vector<vector<int>> line) {
    vector<string> answer;
    set<pair<long long,long long> > s;   // 교집합 쌍들
    // Ax + By + E = 0
    // Cx + Dy + F = 0
    // x = (BF-ED)/(AD-BC), y = (EC-AF)/(AD-BC)
    // AD-BC == 0 이면 두 직선은 평행 또는 일치
    for(int i = 0; i < line.size(); i++){
        for(int j = i+1; j < line.size(); j++){
            // 분모 AD-BC
            long long denominator = (long long)line[i][0]*line[j][1] - (long long)line[i][1]*line[j][0];
            if(denominator == 0){
                continue;
            }
            double x = (double)((long long)line[i][1]*line[j][2] - (long long)line[i][2]*line[j][1]) / denominator;
            double y = (double)((long long)line[i][2]*line[j][0] - (long long)line[i][0]*line[j][2]) / denominator;
            // 둘 다 정수이면 set에 저장
            if(x == (long long)x && y == (long long)y){
                s.insert(make_pair((long long)x, (long long)y));
            }
        }
    }
    long long min_x = LLONG_MAX, min_y = LLONG_MAX, max_x = LLONG_MIN, max_y = LLONG_MIN;
    for(auto it = s.begin(); it != s.end(); it++){
        if(min_x > it->first){
            min_x = it->first;
        }
        if(max_x < it->first){
            max_x = it->first;
        }
        if(min_y > it->second){
            min_y = it->second;
        }
        if(max_y < it->second){
            max_y = it->second;
        }
    }
    for(long long j = max_y; j >= min_y; j--){
        string str = "";
        for(long long i = min_x; i <= max_x; i++){
            if(s.find({i,j}) != s.end()){
                str += "*";
            }
            else{
                str += ".";
            }
        }
        answer.push_back(str);
    }
    return answer;
}

 

---------------------------------------------------후기----------------------------------------------------

 

이 문제에서의 핵심은

공식을 찾아내는 것과 type casting을 제대로 하는 것이 아니었을까 싶다.

공식은 사실, 문제 아래에 나와있어서 간단하지만, 이걸 못 보고 풀기 시작했을 때에는

저런 곱셈 형태로 공식을 만들지 않고, 나눗셈이 엄청 들어간 형태로 만들어서 온갖 예외처리가 필요했었다.

즉, Ax+By+C=0과 ax+by+c=0에서

y = -(A/B)x-(C/B) = -(a/b)x-(c/b)이고,

x = (c/b-C/B) / (A/B-a/b) 라는 식이 나온다.

여기서 예외처리는 b가 0일 경우, B가 0일 경우, A/B-a/b가 0일 경우를 모두 따로 해줘야하기 때문에 복잡하다.

따라서, 이 식을 곱하기 형태로 고친

x = (cB-Cb) / (Ab-aB)라는 식이 문제 아래에 제공된다. (이걸 빨리 봤으면 2시간은 절약했을 것 같지만, 그래도 이런 발상이 존재한다는 것을 배운 것에 의미를...)

 

다음은 type casting 문제이다.

double x = (double)(line[i][1]*line[j][2] - line[i][2]*line[j][1]) / denominator;
double y = (double)(line[i][2]*line[j][0] - line[i][0]*line[j][2]) / denominator;

여기서 분자를 계산하는 부분에 type casting이 쓰인다.

x = (BF-ED)/(AD-BC), y = (EC-AF)/(AD-BC)에서

BF-ED와 EC-AF는 int로 결과가 나오므로, 이 값을 double로 바꿔야 한다.

그리고 이 값을 int인 denominator로 나누더라도, 결과는 double인 x에 저장되므로 자동으로 type casting이 이루어진다.

 

두 번째로 쓰이는 곳은, x와 y값을 계산하는 과정에서 long long으로 바꿔줘야 한다는 것이다.

바꾸지 않으면 29번 테스트케이스만 틀리는 경험을 할 수 있다.(맞왜틀..)

728x90
반응형

'알고리즘' 카테고리의 다른 글

[programmers] 빛의 경로 사이클  (0) 2023.08.26
[programmers] 전력망을 둘로 나누기  (0) 2023.06.22
[programmers] n^2 배열 자르기  (1) 2023.06.16
[programmers] 카운트 다운  (0) 2023.06.15
[programmers] 아이템 줍기  (2) 2023.06.09