본문 바로가기

프로그래머스

Algorithm[공부 : 교점에 별만들기 ]

반응형

교점에 별 만들기

문제 설명

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 크기 이내에서 표현됩니다.
  • 별이 한 개 이상 그려지는 입력만 주어집니다.

 

입출력 예
line result
[[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]] ["*"]
[[1, -1, 0], [2, -1, 0], [4, -1, 0]] ["*"]

입출력 예 설명

입출력 예 #1

문제 예시와 같습니다.

입출력 예 #2

직선 y = 1, x = 1, x = -1는 다음과 같습니다.

(-1, 1), (1, 1) 에서 교점이 발생합니다.

따라서 정답은

"*.*"  

입니다.

입출력 예 #3

직선 y = x, y = 2x는 다음과 같습니다.

(0, 0) 에서 교점이 발생합니다.

따라서 정답은

"*"  

입니다.

입출력 예 #4

직선 y = x, y = 2x, y = 4x는 다음과 같습니다.

(0, 0) 에서 교점이 발생합니다.

따라서 정답은

"*"

입니다.


참고 사항

Ax + By + E = 0
Cx + Dy + F = 0
두 직선의 교점이 유일하게 존재할 경우, 그 교점은 다음과 같습니다.

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

 

내가처음 푼 코드

function GetIntersection([A, B, E], [C, D, F]) {
  if (A * D - B * C === 0) {
    return false;
  }
  const x = (B * F - E * D) / (A * D - B * C);
  if (!Number.isInteger(x)) {
    return false;
  }
  const y = (E * C - A * F) / (A * D - B * C);
  if (!Number.isInteger(y)) {
    return false;
  }
  return [x, y];
}

function solution(line) {
  // 교점 조건
  // 교점들 중 절대값이 가장 큰것이 point임
  // 위치가 정수인 것만 인정

  //교점 구하기
  let intersectionArr = [];
  for (let i = 0; i < line.length - 1; i++) {
    for (let j = i + 1; j < line.length; j++) {
      console.log('i:', i);
      console.log('j:', j);
      console.log('line[i]:', line[i]);
      console.log('line[j]:', line[j]);
      let intersection = GetIntersection(line[i], line[j]);
      if (intersection) {
        console.log('정수교점 있음:', intersection);
        intersectionArr.push(intersection);
      } else {
        console.log('없음');
      }
      console.log();
    }
  }
  console.log('intersectionArr:', intersectionArr);

  // 인자를 사용해서 별 그리기
  // 높이 y값중 가장 큰수 - 가장 작은수
  // 너비 x값중 가장 큰수 - 가장 작은수
  //                큰수                 , 작은수
  let xBigSmall = [intersectionArr[0][0], intersectionArr[0][0]];
  let yBigSmall = [intersectionArr[0][1], intersectionArr[0][1]];
  for (let i = 0; i < intersectionArr.length; i++) {
    console.log('for inner:', intersectionArr[i]);
    if (intersectionArr[i][0] > xBigSmall[0])
      xBigSmall[0] = intersectionArr[i][0];
    if (intersectionArr[i][0] < xBigSmall[1])
      xBigSmall[1] = intersectionArr[i][0];
    if (intersectionArr[i][1] > yBigSmall[0])
      yBigSmall[0] = intersectionArr[i][1];
    if (intersectionArr[i][1] < yBigSmall[1])
      yBigSmall[1] = intersectionArr[i][1];
    console.log('in xBigSmall:', xBigSmall);
    console.log('in yBigSmall:', yBigSmall);
    console.log();
  }
  console.log('xBigSmall:', xBigSmall);
  console.log('yBigSmall:', yBigSmall);

  // matrix만들기
  let rowLen = yBigSmall[0] - yBigSmall[1] + 1;
  let colLen = xBigSmall[0] - xBigSmall[1] + 1;
  let matrix = [];
  for (let i = 0; i < rowLen; i++) {
    matrix.push(Array(colLen).fill('.'));
  }
  console.log('matrix:', matrix);

  // 해당위치에 결과만들기
  for (let i = 0; i < intersectionArr.length; i++) {
    let x = intersectionArr[i][0];
    let y = intersectionArr[i][1];
    console.log('x:', x);
    console.log('y:', y);
    console.log('x - xBigSmall[1]:', x - xBigSmall[1]);
    console.log('y - yBigSmall[1]:', y - yBigSmall[1]);
    // ! 틀림
    // matrix[rowLen - 1 - (y - yBigSmall[1])][colLen - 1 - (x - xBigSmall[1])] =
    //   '*';

    // ? 수정
    matrix[yBigSmall[0] - y][x - xBigSmall[1]] = '*';
    console.log();
  }

  console.log('result matrix:', matrix);
  return matrix.map((e) => e.join(''));
}
정확성 테스트
테스트 1 〉 통과 (0.39ms, 30.2MB)
테스트 2 〉 통과 (8.78ms, 34.9MB)
테스트 3 〉 통과 (0.26ms, 30.2MB)
테스트 4 〉 통과 (21.60ms, 42.2MB)
테스트 5 〉 통과 (4.42ms, 32.9MB)
테스트 6 〉 통과 (1.21ms, 30.3MB)
테스트 7 〉 통과 (6.52ms, 33.9MB)
테스트 8 〉 통과 (0.26ms, 30.1MB)
테스트 9 〉 통과 (23.45ms, 35.1MB)
테스트 10 〉 통과 (32.02ms, 35.6MB)
테스트 11 〉 통과 (36.37ms, 35.9MB)
테스트 12 〉 통과 (28.80ms, 35.8MB)
테스트 13 〉 통과 (37.52ms, 35.7MB)
테스트 14 〉 통과 (33.16ms, 35.3MB)
테스트 15 〉 통과 (37.91ms, 35.8MB)
테스트 16 〉 통과 (29.06ms, 35.4MB)
테스트 17 〉 통과 (25.75ms, 35.3MB)
테스트 18 〉 통과 (29.50ms, 35.8MB)
테스트 19 〉 통과 (27.02ms, 35.7MB)
테스트 20 〉 통과 (30.75ms, 35.3MB)
테스트 21 〉 통과 (33.32ms, 37.8MB)
테스트 22 〉 통과 (0.28ms, 30.1MB)
테스트 23 〉 통과 (0.25ms, 30.2MB)
테스트 24 〉 통과 (0.21ms, 30.1MB)
테스트 25 〉 통과 (0.29ms, 30.1MB)
테스트 26 〉 통과 (0.28ms, 30MB)
테스트 27 〉 통과 (0.16ms, 30MB)
테스트 28 〉 통과 (0.21ms, 30.1MB)
테스트 29 〉 통과 (0.19ms, 30.2MB)

틀렸던이유

 - '*'을 넣는 위치가 x와 y가 달랐어야 했는데 같은줄 알고 착각 하였음

 

내가 푼 코드 (수정)

 - 가독성을 높이기위해 수정함

function GetIntersection([A, B, E], [C, D, F]) {
  if (A * D - B * C === 0) {
    return false;
  }
  const x = (B * F - E * D) / (A * D - B * C);
  if (!Number.isInteger(x)) {
    return false;
  }
  const y = (E * C - A * F) / (A * D - B * C);
  if (!Number.isInteger(y)) {
    return false;
  }
  // 정수인 경우
  return [x, y];
}

function solution(line) {
  // 교점 조건
  // 교점들 중 절대값이 가장 큰것이 point임
  // 위치가 정수인 것만 인정

  //교점 구하기
  let intersectionArr = [];
  for (let i = 0; i < line.length - 1; i++) {
    for (let j = i + 1; j < line.length; j++) {
      let intersection = GetIntersection(line[i], line[j]);
      if (intersection) {
        intersectionArr.push(intersection);
    }
  }
  let xMin = Math.min(...intersectionArr.map((ele) => ele[0]));
  let xMax = Math.max(...intersectionArr.map((ele) => ele[0]));
  let yMin = Math.min(...intersectionArr.map((ele) => ele[1]));
  let yMax = Math.max(...intersectionArr.map((ele) => ele[1]));

  // matrix만들기
  let rowLen = yMax - yMin + 1;
  let colLen = xMax - xMin + 1;
  let matrix = [];
  for (let i = 0; i < rowLen; i++) {
    matrix.push(Array(colLen).fill('.'));
  }

  // 해당위치에 결과만들기
  for (let i = 0; i < intersectionArr.length; i++) {
    let x = intersectionArr[i][0];
    let y = intersectionArr[i][1];
    matrix[yMax - y][x - xMin] = '*';
  }

  console.log('result matrix:', matrix);
  return matrix.map((e) => e.join(''));
}
정확성 테스트
테스트 1 〉 통과 (0.37ms, 29.9MB)
테스트 2 〉 통과 (7.45ms, 34.6MB)
테스트 3 〉 통과 (0.29ms, 29.7MB)
테스트 4 〉 통과 (17.52ms, 41.8MB)
테스트 5 〉 통과 (4.54ms, 32.5MB)
테스트 6 〉 통과 (1.13ms, 30.1MB)
테스트 7 〉 통과 (6.35ms, 33.7MB)
테스트 8 〉 통과 (0.26ms, 29.8MB)
테스트 9 〉 통과 (48.16ms, 34.9MB)
테스트 10 〉 통과 (53.28ms, 34.8MB)
테스트 11 〉 통과 (56.76ms, 35.2MB)
테스트 12 〉 통과 (49.07ms, 35.7MB)
테스트 13 〉 통과 (35.71ms, 35.1MB)
테스트 14 〉 통과 (29.13ms, 35.3MB)
테스트 15 〉 통과 (33.79ms, 35.9MB)
테스트 16 〉 통과 (26.70ms, 35.6MB)
테스트 17 〉 통과 (25.63ms, 35MB)
테스트 18 〉 통과 (32.94ms, 35.7MB)
테스트 19 〉 통과 (26.05ms, 35.6MB)
테스트 20 〉 통과 (27.75ms, 35.5MB)
테스트 21 〉 통과 (31.47ms, 37.6MB)
테스트 22 〉 통과 (0.29ms, 29.8MB)
테스트 23 〉 통과 (0.26ms, 30.1MB)
테스트 24 〉 통과 (0.23ms, 30MB)
테스트 25 〉 통과 (0.24ms, 30MB)
테스트 26 〉 통과 (0.29ms, 29.9MB)
테스트 27 〉 통과 (0.29ms, 29.9MB)
테스트 28 〉 통과 (0.21ms, 29.7MB)
테스트 29 〉 통과 (0.18ms, 30MB)

 

++reference

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

'프로그래머스' 카테고리의 다른 글

프로그래머스 [공부 : 다리를 지나는 트럭]  (0) 2022.06.08