알고리즘🅰/프로그래머스

[프로그래머스] [1차] 다트 게임

개발조각 2022. 3. 13. 16:27
728x90
반응형

예전에 풀다가 포기한 문제였는데 이 문제 뺀 나머지 Level 1문제 다 풀고 다시 풀어보니까 풀리더라고요😆

 

이렇게 보니까 문제가 엄청 길긴 하네요;;;

 

이 문제에 대한 팁에 대해 설명하자면

입력 형식에서 "점수는 0에서 10 사이의 정수이다."라는 말이 있습니다.

이 말은 0 ~ 10까지 존재한다는 말입니다.

 

저는 이번 문제를 풀면서 저 10이라는 두 자리 숫자 때문에 정확성 테스트에서 애를 먹었습니다.

이때 이 아래 있는 테스트 케이스에 추가하시고 푸시면 쉽게 풀 수 있습니다.

dartResult : 10D4S10D
answer : 204


해결방안

function solution(dartResult) {
	// dartResult문자열을 문자하나하나로 나누어서 배열로 만들기
    let spArr = let spArr = [...dartResult];
    
    // '1', '0'이면 '10'으로 바꾸어주기
    let arr = [];
    for(let i=0; i<spArr.length; i++){
        if(spArr[i] === '1' && spArr[i+1] === '0'){
            arr.push('10');
            i+=2;
        }
        arr.push(spArr[i]);        
    }
    
    // 문자열 3세트 각각 점수구하기
    let arr1 = [];
    for(let i=0; i<arr.length; i++){
        if(arr[i] === 'S'|| arr[i] === 'D' || arr[i] === 'T'){
            let num = 0;
            if(arr[i] === 'S') num = Math.pow(arr[i-1]/1, 1);
            else if(arr[i] === 'D') num = Math.pow(arr[i-1]/1, 2);
            else if(arr[i] === 'T') num = Math.pow(arr[i-1]/1, 3);
            
            if(arr[i+1] === '#'){
                if(arr[i+4] === '*') num *= -2;
                else num *= -1;
            } 
            if(arr[i+1] === '*'){
                if(arr[i+4] === '*') num *= 4;
                else num *= 2;
            }else if(arr[i+3] === '*') num *= 2;
            
            arr1.push(num);
            num = 0;
        }
    }
    
    // 문자열 3세트 점수 합산하기
    return arr1.reduce((acc, cur) => acc+cur);
}

 

해결방안 순서

  1. dartResult문자열을 문자 하나하나로 나누어서 배열로 만들기
  2. '1', '0'이면 '10'으로 바꾸어주기
  3. 문자열 3세트 각각 점수 구하기
  4. 문자열 3세트 점수 합산하기

 

1단계. dartResult문자열을 문자 하나하나로 나누어서 배열로 만들기

// 1단계. dartResult문자열을 문자하나하나로 나누어서 배열로 만들기
let spArr = [...dartResult];

점수를 구하기 위해서는 dartResult문자열을 배열로 쪼개는 게 구하게 쉽다는 생각이 들었습니다.

해봤자 뒤에서 for문을 쓸 예정이니까 미리 배열로 만들면 편하겠죠?

 

문자열을 배열로 만드는 건 원래라면 split()메서드를 사용했지만

이번에는 조금 다른 방법으로 바꾸어 볼까 합니다.

 

spread operator문법을 사용해서 바꾸었는데요.

spread operator문법은 쉽게 설명하면 괄호를 제거해주는 연산자입니다.

spread는 배열뿐만 아니라 문자, 객체에도 쓸 수 있습니다.

 

문자열에다가 spread를 붙이면 문자열.split('')와 비슷한 효과를 만들 수 있습니다.

let 문자 = 'hello';
console.log(...문자); // 'h', 'e', 'l', 'l', 'o'
console.log([...문자]; // ['h', 'e', 'l', 'l', 'o']

 

테스트 1 (dartResult = 1S2D*3T)
let spArr = [...dartResult]; -> ['1', 'S', '2', 'D', '*', '3', 'T']

테스트 2 (dartResult = 1D2S#10S)
let spArr = [...dartResult]; -> ['1', 'D', '2', 'S', '#', '1', '0', 'S']
.
.
.

 

2단계. '1', '0'이면 '10'으로 바꾸어주기

// 2단계. '1', '0'이면 '10'으로 바꾸어주기
let arr = [];
for(let i=0; i<spArr.length; i++){
    if(spArr[i] === '1' && spArr[i+1] === '0'){
        arr.push('10');
        i+=2;
    }
    arr.push(spArr[i]);        
}

이 문제에서는 숫자가 1~9까지 존재하는 게 아니라 10까지 존재합니다.

그래서 '1', '0'을 '10'으로 만들어주는 작업을 해야 됩니다.

 

spArr에 배열 원소가 '1'인데 다음번째 원소가 '0'이면 1,0이 아닌 10이겠죠?

for문을 사용해서 spArr[i]가 '1'이고 spArr[i+1]가 '0' 이면 '10'을 만들어 주었습니다.

  • for(let i=0; i<spArr.length; i++) : 0부터 spArr.length-1 만큼 반복
  • if(spArr[i] === '1' && spArr[i+1] === '0') : spArr[i]가 '1'이고 spArr[i+1]가 '0' 이면
  • arr.push('10'); : '1', '0'이면 '10'으로 넣어주기
  • i+=2; : '1', '0'이 맞으면 2개를 합쳐서 '10'으로 쓴 거기 때문에 i번째 다음 꺼는 할 필요가 없으므로 i+2를 해주었습니다.
  • arr.push(spArr[i]); : spArr[i]가 1이 아니면 나머지는 그대로 넣어주었습니다. (else 넣어서 하시면 안돼요)

 

테스트 2 (dartResult = '1D2S#10S';)
1단계 : let spArr = [...dartResult]; -> ['1', 'D', '2', 'S', '#', '1', '0', 'S']
2단계 : arr = ['1', 'D', '2', 'S', '#', '10', 'S']

테스트 8 (dartResult = '10D4S10D') // 따로 만든 거
1단계 : let spArr = [...dartResult]; -> [ '1', '0', 'D', '4', 'S', '1', '0', 'D']
2단계 : arr = [ '10', 'D', '4', 'S', '10', 'D' ]

 

3단계. 문자열 3세트 각각 점수 구하기

// 3단계. 문자열 3세트 각각 점수구하기
let arr1 = [];
for(let i=0; i<arr.length; i++){
    if(arr[i] === 'S'|| arr[i] === 'D' || arr[i] === 'T'){
        let num = 0;
        if(arr[i] === 'S') num = Math.pow(arr[i-1]/1, 1);
        else if(arr[i] === 'D') num = Math.pow(arr[i-1]/1, 2);
        else if(arr[i] === 'T') num = Math.pow(arr[i-1]/1, 3);

        if(arr[i+1] === '#'){
            if(arr[i+4] === '*') num *= -2;
            else num *= -1;
        } 
        if(arr[i+1] === '*'){
            if(arr[i+4] === '*') num *= 4;
            else num *= 2;
        }else if(arr[i+3] === '*') num *= 2;

        arr1.push(num);
        num = 0;
    }
}

 

 

Single(S), Double(D), Triple(T) 영역이 존재

  • 영역 당첨 시 점수에서 1제곱, 2제곱, 3제곱 (점수1 , 점수2 , 점수3 )으로 계산

옵션으로 스타상(*) , 아차상(#)이 존재(점수가 ABC 라면 해당점수는 B로 예시)

  • 스타상(*) 당첨 시 해당 점수(B)와 바로 전에 얻은 점수(A)를 각 2배로 만든다. -> A B* C이면 A*2 B*2 C
  • 아차상(#) 당첨 시 해당 점수(B)는 마이너스된다. -> A B# C이면 A B(-1) C
  • 스타상(*)의 효과는 다른 스타상(*)의 효과와 중첩될 수 있다. 이 경우 중첩된 스타상(*) 점수는 4배가 된다. (테스트 4 참고) -> A* B*C이면 A*4 B* C
  • 스타상(*)의 효과는 아차상(#)의 효과와 중첩될 수 있다. 이 경우 중첩된 아차상(#)의 점수는 -2배가 된다. (테스트 5번 참고) -> A# B* C이면 A(-1)*2 B*2 C

이 위에 있는걸 다 적용되게 만들어야 됩니다.

 

 

2단계에서 만든 arr배열을 보시면

  • 테스트 1 arr = ['1', 'S', '2', 'D', '*', '3', 'T']
  • 테스트 2 arr = ['1', 'D', '2', 'S', '#', '10', 'S']
  • 테스트 3 arr = [ '1', 'D', '2', 'S', '0', 'T' ]

'S', 'D', 'T'앞에는 무조건 숫자이고, 뒤는 '*', '#'이거나 없을 수 있습니다.

그래서 'S', 'D', 'T'을 기준으로 점수를 계산했습니다.

 

'S', 'D', 'T'의 앞에 자리가 무조건 숫자인으로 숫자와 'S', 'D', 'T'를 사용해서 1제곱, 2제곱, 3제곱을 만들어주면 되는데

'*', '#'은 상황에 따라 달라집니다.

그래서 어떻게 생각했다면

'*'은 현재 자리+1부터 '*'이 몇 개인은 지를 확인하면 되고

-> 내 파트, 내 다음 파트에 '*'이 있네?,

->내 파트 말고 다음 파트에 '*'이 있네?

-> 내 파트, 내 다음 파트 둘 다 '*'이 있네

 

'#'은 현재 자리+1에 있는지 확인하고 그다음에 *이 있는지 확인하면 됩니다.

-> 내 파트에 '#'이 있네?

-> 내파트에 '#'이 있는데 다음 파트에 '*'이 있네

 

'*' 스타상일 때 ('S', 'D', 'T'위치가 i)

  1. i+1에 '*' 있다면 -> 내 파트에 '*'이 있네
  2. i+1, i+4 두 곳에 '*'이 있다면 -> 내 파트와 내 다음 파트에 '*'이 있네
  3. i+1에는 '*' 없고 i+3에 '*'이 있다면 -> 내 다음 파트에만 '*'이 있네

이렇게 3가지의 경우로 나누어집니다.

 

코드로 작성할 때 예시

  1. i+1에 '*' 있다면 
    1. i+4에 '*' 있다면
      1. true : num*4
      2. false : num*2
  2. i+1에는 '*' 없고 i+3에 '*'이 있다면
    1. true : num*2해주기

 

'#' 아차상일 때 ('S', 'D', 'T'위치가 i)

  1. i+1에 '#'에만 있다면
  2. i+1에 '#'이 있는데 i+4에 '*'이 있다면

이렇게 2가지 경우로 나누어집니다.

 

코드로 작성할 때 예시

  1. i+1에 '#' 있다면 
    1. i+4에 '*' 있다면
      1. true : num*(-2)
      2. false : num*(-1)

 

이 '*'스타상 '#'아차상일 때 코드로 작성할 때 예시를 코드로 작성하면 됩니다.

 

 

코드가 길어서 복잡해 보이지만 순서를 간단하게 설명하자면

3단계 코드 순서

  1. 0부터 arr배열의 길이만큼 for문으로 반복해준다.
  2. arr배열(2단계에서 만든 배열)에서 'S', 'D', 'T'를 찾기
  3. 'S', 'D', 'T'의 앞에 자리(i-1), arr[i-1]의 1제곱, 2제곱, 3제곱을 만들어서 num에다가 담기
  4. 'S', 'D', 'T'의 뒤에 자리(i+1), arr[i+1]이 '#'이 있다면에 대한 코드넣기(위에 '#' 아차상일 때 내용)
  5. 'S', 'D', 'T'의 뒤에 자리(i+1), arr[i+1]이 '*'이 있다면에 대한 코드넣기(위에 '*' 스타상일 때 내용)
  6. arr1에 2~5에서 해준 num의 값을 넣고 num초기화해주기

 

3단계_1. 0부터 arr배열의 길이만큼 for문으로 반복해준다.

for(let i=0; i<arr.length; i++)

 

3단계_2. arr배열(2단계에서 만든 배열)에서 'S', 'D', 'T'를 찾기

if(arr[i] === 'S'|| arr[i] === 'D' || arr[i] === 'T'){
	let num = 0;
}

'S', 'D', 'T'에서만 num을 계산해 줄 거기 때문에 arr[i]가 'S', 'D', 'T'에서만 작동하도록 했습니다.

각 파트의 합산을 구하기 위해 num=0;을 넣어주었고

num이 arr[i]가 'S', 'D', 'T'에서만 계산할 수 있도록 해주었습니다.

 

3단계_3. 'S', 'D', 'T'의 앞에 자리(i-1), arr[i-1]의 1제곱, 2제곱, 3제곱을 만들어서 num에다가 담기

if(arr[i] === 'S') num = Math.pow(arr[i-1]/1, 1);
else if(arr[i] === 'D') num = Math.pow(arr[i-1]/1, 2);
else if(arr[i] === 'T') num = Math.pow(arr[i-1]/1, 3);

문제에서 S이면 1제곱, D이면 2제곱, T이면 3제곱을 하라 했습니다.

?제곱을 하기 위해 Math.pow()함수를 썼습니다.

MDN Web Docs
Math.pow()함수는 base^exponent처럼 base에 exponent를 제곱한 값을 반환합니다.

문법 : Math.pow(base, exponent)

  • base : 밑 값.
  • exponent : 밑 을 제곱하기 위해 사용하는 지수.
// Math.pow()함수 예제
// 간단한 예
Math.pow(7, 2);    // 49
Math.pow(7, 3);    // 343
Math.pow(2, 10);   // 1024

 

Math.pow()함수를 사용해서 각각 문자마다 알맞은 제곱을 해주었습니다.

이때 arr[i-1]/1을 해준 이유는 arr배열을 보시면 숫자까지 다 ''로 감싸져 있습니다. 즉 문자라는 말입니다.

문자를 숫자로 바꾸어야 1제곱, 2제곱, 3제곱을 해줄 수 있습니다.

 

문자를 숫자로 바꾸는 방법으로는 전에도 말했지만 

"문자열과 숫자열의 사칙연산"으로 구해주면 쉽게 구할 수 있습니다. 

문자열과 숫자열의 사칙연산은 숫자가 나오게 됩니다.

  • '5'/1 = 5

 

3단계_4. 'S', 'D', 'T'의 뒤에 자리(i+1), arr[i+1]이 '#'이 있다면에 대한 코드 넣기(위에'#' 아차상일 때 내용)

if(arr[i+1] === '#'){
    if(arr[i+4] === '*') num *= -2;
    else num *= -1;
}
  1. i+1에 '#' 있다면 
    1. i+4에 '*' 있다면
      1. true : num*(-2)
      2. false : num*(-1)

이 내용을 코드로 작성했습니다.

 

3단계_5. 'S', 'D', 'T'의 뒤에 자리(i+1), arr[i+1]이 '*'이 있다면에 대한 코드 넣기(위에'*' 스타상일 때 내용)

if(arr[i+1] === '*'){
    if(arr[i+4] === '*') num *= 4;
    else num *= 2;
}else if(arr[i+3] === '*') num *= 2;
  1. i+1에 '*' 있다면 
    1. i+4에 '*' 있다면
      1. true : num*4
      2. false : num*2
  2. i+1에는 '*' 없고 i+3에 '*'이 있다면
    1. true : num*2해주기

이 내용을 코드로 작성했습니다.

 

3단계_6. arr1에 2~5에서 해준 num의 값을 넣고 num초기화해주기

arr1.push(num);
num = 0;

1~5에서 만든 num값을 arr1에 담아주고, num을 초기화해주었습니다.

num은 다음번 arr[i]에서 'S', 'D', 'T'이 나오면 그 위치에서 num의 값을 구해야 되기 때문에 다시 초기화해주었습니다.

 

3단계 코드 순서

테스트 1 arr = ['1', 'S', '2', 'D', '*', '3', 'T']
i = 1, 3, 6일 때 'S', 'D', 'T'가 있음
2. i=1일 때 'S'가 있음
3. arr[i-1] = arr[1] ='1' -> 1에 1제곱은 1 -> num = 1
5. arr[i+3] = arr[4] = '*' -> num *= 2 -> num = 1*2 = 2
6.  arr1 = [num] -> arr1=[2]; num=0;

2. i=3일 때 'D'가 있음
3. arr[i-1] = arr[2] ='2' -> 2에 2제곱은 4 -> num = 4
5. arr[i+1] = arr[4] = '*' -> num *= 2 -> num = 4*2 = 8
6.  arr1 = [num] -> arr1=[2, 8]; num=0;

2. i=6일 때 'T'가 있음
3. arr[i-1] = arr[5] ='3' -> 3에 3제곱은 27 -> num = 27
6.  arr1 = [num] -> arr1=[2, 8, 27]; num=0;

arr1 = [2, 8, 27]

 

 

4단계. 문자열 3세트 점수 합산하기

// 4단계. 문자열 3세트 점수 합산하기
return arr1.reduce((acc, cur) => acc+cur);

마지막으로 arr1에 원소 값을 합산하면 되는데요. 배열 안 원소의 합을 구할 때는 reduce()메서드를 사용하면 쉽게 구할 수 있습니다.

 

MDN Web Docs
reduce() 메서드는 배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결과값을 반환합니다.

reduce() 함수는 네 개의 인자를 가집니다.
1. 누산기 (acc)
2. 현재 값 (cur)
3. 현재 인덱스 (idx)
4. 원본 배열 (src)

구문 : 배열.reduce((callback)=>처리할 현재 요소, 기본값)

  • callback : 배열의 각 요소에 대해 실행할 함수. 다음 네 가지 인수(acc, cur, idx, src)를 받습니다.
  • 기본값 : 넣어도 되고 안 넣어도 됨
// reduce() 메서드 예제
const array1 = [1, 2, 3, 4];
const reducer = (previousValue, currentValue) => previousValue + currentValue; // 1 + 2 + 3 + 4
console.log(array1.reduce(reducer)); // 10

 

3단계에서 구한 테스트 1 arr1 = [ 1, 8, 27 ]
return arr1.reduce((acc, cur) => acc+cur); -> 1+ 8 + 27 = 37

여기까지 프로그래머스 [1차] 다트 게임 해결방안에 대해 설명해보았습니다.

728x90
반응형