본문 바로가기

코딩 테스트(Coding test)/Lv. 2

[프로그래머스/C++] 교점에 별 만들기

미숙한 블로그 주인이 코딩테스트 문제를 풀어가는 과정을 담은 글입니다. 이 풀이가 효율적인 풀이가 아닐 수 있으며, 부정확한 정보가 많이 있을 수 있습니다. 보완해야할 점이 있다면 댓글로 남겨주세요!

 

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

 

코딩테스트 연습 - 교점에 별 만들기

[[2, -1, 4], [-2, -1, 4], [0, -1, 1], [5, -8, -12], [5, 8, 12]] ["....*....", ".........", ".........", "*.......*", ".........", ".........", ".........", ".........", "*.......*"] [[0, 1, -1], [1, 0, -1], [1, 0, 1]] ["*.*"] [[1, -1, 0], [2, -1, 0], [4, -

programmers.co.kr

교점에 별 만들기

문제

Ax + By + C = 0으로 표현할 수 있는 n개의 직선이 주어질 때, 이 직선의 교점 중 정수 좌표에 별을 그리려 합니다.

예를 들어, 다음과 같은 직선 5개를

  • 2x - y + 4 = 0
  • -2x - y + 4 = 0
  • -y + 1 = 0
  • 5x - 8y - 12 = 0
  • 5x + 8y + 12 = 0

좌표 평면 위에 그리면 아래 그림과 같습니다.

이때, 모든 교점의 좌표는 (4, 1), (4, -4), (-4, -4), (-4, 1), (0, 4), (1.5, 1.0), (2.1, -0.19), (0, -1.5), (-2.1, -0.19), (-1.5, 1.0)입니다. 이 중 정수로만 표현되는 좌표는 (4, 1), (4, -4), (-4, -4), (-4, 1), (0, 4)입니다.

만약 정수로 표현되는 교점에 별을 그리면 다음과 같습니다.

위의 그림을 문자열로 나타낼 때, 별이 그려진 부분은 *, 빈 공간(격자선이 교차하는 지점)은 .으로 표현하면 다음과 같습니다.

"..........."  
".....*....."  
"..........."  
"..........."  
".*.......*."  
"..........."  
"..........."  
"..........."  
"..........."  
".*.......*."  
"..........."  

이때 격자판은 무한히 넓으니 모든 별을 포함하는 최소한의 크기만 나타내면 됩니다.

따라서 정답은

"....*...."  
"........."  
"........."  
"*.......*"  
"........."  
"........."  
"........."  
"........."  
"*.......*"  

입니다.

직선 A, B, C에 대한 정보가 담긴 배열 line이 매개변수로 주어집니다. 이때 모든 별을 포함하는 최소 사각형을 return 하도록 solution 함수를 완성해주세요.

 

제한사항

  • line의 세로(행) 길이는 2 이상 1,000 이하인 자연수입니다.
    • line의 가로(열) 길이는 3입니다.
    • line의 각 원소는 [A, B, C] 형태입니다.
    • A, B, C는 -100,000 이상 100,000 이하인 정수입니다.
    • 무수히 많은 교점이 생기는 직선 쌍은 주어지지 않습니다.
    • A = 0이면서 B = 0인 경우는 주어지지 않습니다.
  • 정답은 1,000 * 1,000 크기 이내에서 표현됩니다.
  • 별이 한 개 이상 그려지는 입력만 주어집니다.

 

풀이

입출력 예시

 

풀이

먼저 교점이 존재하는지, 존재하면 어디인지를 확인할 방법은 문제의 마지막에 주어져있다.

 

 

AD - BC = 0인 경우 두 직선은 평행 또는 일치한다.

 

이를 이용해 AD - BC 가 0이 아니면서 교점이 정수일 때에만 그 위치에 '*'을 찍어준다. answer에 표현할 때에는 x는 작은 값, y는 큰 값부터 적용되어야 한다.

 

주의할 사항은 A, B, C가 -100,000 이상 100,000 이하의 수가 들어온다는 점이다. 즉, 모든 곱셈의 결과가 int 형의 최댓값(또는 최솟값)을 넘기는 경우가 발생할 수 있다는 점이다. 그러므로, long long 형을 사용해 오버플로우(또는 언더플로우)에 대비해야 한다. (27번과 28번이 아마 이 부분에 관한 테스트 케이스 였던 것 같다.)

 

#include <string>
#include <vector>
#include <set>
#include <utility>
#include <algorithm>

using namespace std;

vector<string> solution(vector<vector<int>> line) {
    vector<string> answer;
    set<pair<long long, long long>> pos;

    for (int i = 0; i < line.size() - 1; i++) {
        for (int j = i + 1; j < line.size(); j++) {
            long long AD_BC = (long long)line[i][0] * line[j][1] - (long long)line[i][1] * line[j][0];
            if (AD_BC != 0) {
                double x = ((double)line[i][1] * line[j][2] - (double)line[i][2] * line[j][1]) / AD_BC;
                double y = ((double)line[i][2] * line[j][0] - (double)line[i][0] * line[j][2]) / AD_BC;

                if (x - (long long)x == 0 && y - (long long)y == 0) {
                    pos.insert({ x, y });
                }
            }
        }
    }

    long long min_x = 9223372036854775807;
    long long max_x = -9223372036854775808;
    long long min_y = 9223372036854775807;
    long long max_y = -9223372036854775808;

    for (const auto& p : pos) {
        min_x = min(p.first, min_x);
        min_y = min(p.second, min_y);
        max_x = max(p.first, max_x);
        max_y = max(p.second, max_y);
    }

    for (long long y = min_y; y <= max_y; y++) {
        string tmp = "";
        for (long long x = min_x; x <= max_x; x++) {
            tmp += ".";
        }
        answer.push_back(tmp);
    }

    for (const auto& p : pos) {
        answer[abs(p.second - max_y)][abs(p.first - min_x)] = '*';
    }

    return answer;
}