Light Blue Pointer
본문 바로가기
Coding Test

[프로그래머스]기사단원의 무기

by Greedy 2023. 11. 30.

문제 주소

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

 

프로그래머스

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

programmers.co.kr

문제 설명

숫자나라 기사단의 각 기사에게는 1번부터 number까지 번호가 지정되어 있습니다. 기사들은 무기점에서 무기를 구매하려고 합니다.

각 기사는 자신의 기사 번호의 약수 개수에 해당하는 공격력을 가진 무기를 구매하려 합니다. 단, 이웃나라와의 협약에 의해 공격력의 제한수치를 정하고, 제한수치보다 큰 공격력을 가진 무기를 구매해야 하는 기사는 협약기관에서 정한 공격력을 가지는 무기를 구매해야 합니다.

예를 들어, 15번으로 지정된 기사단원은 15의 약수가 1, 3, 5, 15로 4개 이므로, 공격력이 4인 무기를 구매합니다. 만약, 이웃나라와의 협약으로 정해진 공격력의 제한수치가 3이고 제한수치를 초과한 기사가 사용할 무기의 공격력이 2라면, 15번으로 지정된 기사단원은 무기점에서 공격력이 2인 무기를 구매합니다. 무기를 만들 때, 무기의 공격력 1당 1kg의 철이 필요합니다. 그래서 무기점에서 무기를 모두 만들기 위해 필요한 철의 무게를 미리 계산하려 합니다.

기사단원의 수를 나타내는 정수 number와 이웃나라와 협약으로 정해진 공격력의 제한수치를 나타내는 정수 limit와 제한수치를 초과한 기사가 사용할 무기의 공격력을 나타내는 정수 power가 주어졌을 때, 무기점의 주인이 무기를 모두 만들기 위해 필요한 철의 무게를 return 하는 solution 함수를 완성하시오.


제한사항

  • 1 ≤ number ≤ 100,000
  • 2 ≤ limit ≤ 100
  • 1 ≤ power ≤ limit

입출력 예

number limit power result

5 3 2 10
10 3 2 21

입출력 예 설명

입출력 예 #1

1부터 5까지의 약수의 개수는 순서대로 [1, 2, 2, 3, 2]개입니다. 모두 공격력 제한 수치인 3을 넘지 않기 때문에 필요한 철의 무게는 해당 수들의 합인 10이 됩니다. 따라서 10을 return 합니다.

입출력 예 #2

1부터 10까지의 약수의 개수는 순서대로 [1, 2, 2, 3, 2, 4, 2, 4, 3, 4]개입니다. 공격력의 제한수치가 3이기 때문에, 6, 8, 10번 기사는 공격력이 2인 무기를 구매합니다. 따라서 해당 수들의 합인 21을 return 합니다.

문제가 현란하군요…

공격력 1당 1kg이라니까 공격력의 합을 리턴하라는 뜻

 

풀이 과정

if not > limit ) 공격력 = 약수 개수

if > limit) 공격력 = power

import java.lang.Math;
class Solution {
    public int solution(int number, int limit, int power) {
        int answer = 0;
        for(int n : number){
            int divisor = 2;//자기 자신과 1은 반드시 약수니까
            for(int i=2;i<Math.sqrt(n)+1;i++){
                if(n%i==0){
                    divisor += 2;//약수는 반드시 쌍임
                }
            }
            if(n%Math.sqrt(n)==0){
                divisor++;
            }
            /*limit check*/
            if(divisor>limit){
                divisor = power;
            }
            answer += divisor;
        }
        
        return answer;
    }
}

뭔소리여

/Solution.java:5: error: for-each not applicable to expression type
        for(int n : number){
                    ^
  required: array or java.lang.Iterable
  found:    int
1 error

아 여태까지 number배열이 넘어오는줄 알았는데 아니었네

걍 int네

1부터 세야되네

import java.lang.Math;
class Solution {
    public int solution(int number, int limit, int power) {
        int answer = 0;
        for(int i = 1; i<=number ; i++){
            int divisor = 2;//자기 자신과 1은 반드시 약수니까
            for(int j=2;i<Math.sqrt(i)+1;j++){
                if(i%j==0){
                    divisor += 2;//약수는 반드시 쌍임
                }
            }
            if(i%Math.sqrt(i)==0){
                divisor++;
            }
            /*limit check*/
            if(divisor>limit){
                divisor = power;
            }
            answer += divisor;
        }
        
        return answer;
    }
}

Exception in thread "main" java.lang.ArithmeticException: / by zero

at Solution.solution(Unknown Source)

at SolutionTest.lambda$main$0(Unknown Source)

at SolutionTest$SolutionRunner.run(Unknown Source)

at SolutionTest.main(Unknown Source)

예? 제가 나누기 연산을 쓴 적이 없는데요

모듈로가 잘못된건가

sqrt를 double으로 받았어야 되나?

double로 받아서 해도 똑같길래 더 바꿔봄

import java.lang.Math;
class Solution {
    public int solution(int number, int limit, int power) {
        int answer = 0;
        for(int i = 1; i<=number ; i++){
            int divisor = 2;//자기 자신과 1은 반드시 약수니까
            double squareRoot = Math.sqrt(i);
            int sqrtInt = (int)squareRoot;
            double decimalPart = squareRoot-sqrtInt;
            
            for(int j=2;i<squareRoot+1;j++){
                if(i%j==0){
                    divisor += 2;//약수는 반드시 쌍임
                }
            }
            
            if(decimalPart==0.0){
                divisor--;
            }
            /*limit check*/
            if(divisor>limit){
                divisor = power;
            }
            answer += divisor;
        }
        
        return answer;
    }
}

Exception in thread "main" java.lang.ArithmeticException: / by zero

at Solution.solution(Unknown Source)

at SolutionTest.lambda$main$0(Unknown Source)

at SolutionTest$SolutionRunner.run(Unknown Source)

at SolutionTest.main(Unknown Source)

아니 진짜 뭔소리예요…

제가 나누기를 언제썼음

The % operator returns the remainder after dividing the first number by the second number. If the second number (in your example size) is zero, then you will get a divide by zero ArithmeticException.

구글링해보니 %도 0으로 나누면 그렇게 된다고 함

아니 근데 %부분도 다 뺐는데요 더하기 빼기로 다 하려고 ㅋㅋ

테스트 1 〉실패 (런타임 에러)테스트 2 〉실패 (런타임 에러)테스트 3 〉실패 (런타임 에러)테스트 4 〉실패 (런타임 에러)

왜이래 도대체

인덱스가 잘못되어서 그렇습니다

for(int j=2;i<squareRoot+1;j++){

for(int j=2;j<squareRoot+1;j++){

import java.lang.Math;
class Solution {
    public int solution(int number, int limit, int power) {
        int answer = 0;
        for(int i = 1; i<=number ; i++){
            int divisor = 2;//자기 자신과 1은 반드시 약수니까
            double squareRoot = Math.sqrt(i);
            int sqrtInt = (int)squareRoot;
            double decimalPart = squareRoot-sqrtInt;
            
            for(int j=2;j<squareRoot+1;j++){
                if(i%j==0){
                    divisor += 2;//약수는 반드시 쌍임
                }
            }            
            if(decimalPart==0.0){
                divisor--;
            }
            /*limit check*/
            if(divisor>limit){
                divisor = power;
            }
            answer += divisor;
        }
        
        return answer;
    }
}

이제 런타임 에러는 안 뜨고 테스트케이스는 다 맞지만

제출하면 수많은 실패가..

import java.lang.Math;
class Solution {
    public int solution(int number, int limit, int power) {
        int answer = 0;
        for(int i = 1; i<=number ; i++){
            int divisor = 2;//자기 자신과 1은 반드시 약수니까
             double squareRoot = Math.sqrt(i);
            // int sqrtInt = (int)squareRoot;
            // double decimalPart = squareRoot-sqrtInt;
            
            for(int j=2;j<squareRoot+1;j++){
                if(i%j==0){
                    divisor += 2;//약수는 반드시 쌍임
                }
            }            
            if(squareRoot%Math.sqrt(i)==0){
                divisor--;
            }
            if(divisor>limit){
                divisor = power;//제한 넘으면 초과기사공격력
            }
            answer += divisor;
        }
        
        return answer;
    }
}
테스트 1입력값 〉5, 3, 2기댓값 〉10실행 결과 〉실행한 결괏값 9이 기댓값 10과 다릅니다.테스트 2입력값 〉10, 3, 2기댓값 〉21실행 결과 〉테스트를 통과하였습니다.

음 전에 본대로 해봤는데 안되네?

걍 내 방식으로 회귀함

import java.lang.Math;
class Solution {
    public int solution(int number, int limit, int power) {
        int answer = 0;
        for(int i = 1; i<=number ; i++){
            int divisor = 2;//자기 자신과 1은 반드시 약수니까
            double squareRoot = Math.sqrt(i);
            int sqrtInt = (int)squareRoot;
            double decimalPart = squareRoot-sqrtInt;
            
            for(int j=2;j<squareRoot+1;j++){
                if(i%j==0){
                    divisor += 2;//약수는 반드시 쌍임
                }
            }            
            if(decimalPart==0.0){
                divisor--;
            }
            if(divisor>limit){
                divisor = power;//제한 넘으면 초과기사공격력
            }
            answer += divisor;
        }
        return answer;//봐도봐도 뭐가틀린지 모르겠네요 ㅎ ㅎㅎ ㅎ ㅎㅎ 
    }
}

짧은 실패부터 긴 실패까지….

테스트 23 〉	실패 (0.04ms, 70.1MB)
테스트 24 〉	실패 (85.59ms, 78.8MB)
테스트 25 〉	실패 (145.89ms, 75.3MB)

뭐가 문제일까

그냥 전통적인 방식의 약수세기로 회귀해봄

import java.lang.Math;
class Solution {
    public int solution(int number, int limit, int power) {
        int answer = 0;
        for(int i = 1; i<=number ; i++){
            int divisor = 0;
            // int divisor = 2;//자기 자신과 1은 반드시 약수니까
            // double squareRoot = Math.sqrt(i);
            // int sqrtInt = (int)squareRoot;
            // double decimalPart = squareRoot-sqrtInt;
            for(int j=1;j<=i;j++){
                if(i%j==0){
                    divisor++;//약수는 반드시 쌍임
                }
            }
            // for(int j=2;j<squareRoot+1;j++){
            //     if(i%j==0){
            //         divisor += 2;//약수는 반드시 쌍임
            //     }
            // }            
            // if(decimalPart==0.0){
            //     divisor--;
            // }
            if(divisor>limit){
                divisor = power;//제한 넘으면 초과기사공격력
            }
            answer += divisor;
        }
        return answer;
    }
}

오 되긴 되는데 개오래걸림

테스트 1 〉 통과 (166.06ms, 94.4MB)

테스트 2 〉 통과 (19.50ms, 79.8MB)
테스트 3 〉 통과 (4.48ms, 73.9MB)
테스트 4 〉 통과 (17.51ms, 77.9MB)
테스트 5 〉 통과 (3.38ms, 72.9MB)
테스트 6 〉 통과 (157.72ms, 88.6MB)
테스트 7 〉 통과 (11.81ms, 77.9MB)
테스트 8 〉 통과 (7.16ms, 70.6MB)
테스트 9 〉 통과 (189.48ms, 80.8MB)
테스트 10 〉 통과 (8.73ms, 77.1MB)
테스트 11 〉 실패 (시간 초과)
테스트 12 〉 실패 (시간 초과)
테스트 13 〉 실패 (시간 초과)
테스트 14 〉 실패 (시간 초과)
테스트 15 〉 실패 (시간 초과)
테스트 16 〉 실패 (시간 초과)
테스트 17 〉 통과 (0.03ms, 79.4MB)
테스트 18 〉 실패 (시간 초과)
테스트 19 〉 통과 (15.49ms, 81MB)
테스트 20 〉 통과 (10.71ms, 78.5MB)
테스트 21 〉 통과 (0.02ms, 75.8MB)
테스트 22 〉 통과 (0.02ms, 82MB)
테스트 23 〉 통과 (0.02ms, 74.8MB)
테스트 24 〉 실패 (시간 초과)
테스트 25 〉 실패 (시간 초과)
테스트 26 〉 통과 (3.00ms, 76.9MB)
테스트 27 〉 통과 (3.02ms, 79MB)

기존의 약수세는 방식이 뭔가 틀렸나봄

import java.lang.Math;
class Solution {
    public int solution(int number, int limit, int power) {
        int answer = 0;
        for(int i = 1; i<=number ; i++){
            double squareRoot = Math.sqrt(i);
            int sqrtInt = (int)squareRoot;
            double decimalPart = squareRoot-sqrtInt;
            
            int divisor = 2;//자기 자신과 1은 반드시 약수니까
            if(decimalPart==0.0){
                divisor--;
            }
            
            
            for(int j=2;j<i/2;j++){
                if(i%j==0){
                    divisor += 2;//약수는 반드시 쌍임
                }
            }            
            
            if(divisor>limit){
                divisor = power;//제한 넘으면 초과기사공격력
            }
            answer += divisor;
        }
        return answer;
    }
}

for(int j=2;j<i/2;j++){ 약수 세는 범위를 증가시켰더니 어떤건 통과됨

테스트 1 〉 실패 (127.40ms, 71.5MB)

테스트 2 〉 실패 (11.74ms, 90.5MB)
테스트 3 〉 실패 (7.54ms, 79.1MB)
테스트 4 〉 실패 (21.27ms, 75.1MB)
테스트 5 〉 실패 (5.21ms, 72.8MB)
테스트 6 〉 실패 (108.48ms, 87.9MB)
테스트 7 〉 실패 (13.15ms, 80.4MB)
테스트 8 〉 실패 (10.61ms, 84.8MB)
테스트 9 〉 실패 (126.06ms, 94.8MB)
테스트 10 〉 실패 (6.69ms, 67.5MB)
테스트 11 〉 실패 (7766.70ms, 76.6MB)
테스트 12 〉 실패 (9290.16ms, 73.1MB)
테스트 13 〉 실패 (시간 초과)
테스트 14 〉 실패 (9022.64ms, 81.1MB)
테스트 15 〉 실패 (9048.51ms, 84.4MB)
테스트 16 〉 실패 (시간 초과)
테스트 17 〉 통과 (0.04ms, 79.9MB)
테스트 18 〉 통과 (9336.94ms, 75.3MB)
테스트 19 〉 실패 (7.45ms, 65.7MB)
테스트 20 〉 실패 (8.26ms, 80.3MB)
테스트 21 〉 통과 (0.03ms, 77.2MB)
테스트 22 〉 통과 (0.04ms, 74MB)
테스트 23 〉 실패 (0.04ms, 75MB)
테스트 24 〉 실패 (9734.58ms, 90.8MB)
테스트 25 〉 실패 (9278.00ms, 78.2MB)
테스트 26 〉 실패 (4.12ms, 71.1MB)
테스트 27 〉 실패 (3.70ms, 84.5MB)
import java.lang.Math;
class Solution {
    public int solution(int number, int limit, int power) {
        int answer = 0;
        for(int i = 1; i<=number ; i++){
            int divisor = 2;//자기 자신과 1은 반드시 약수니까
            double squareRoot = Math.sqrt(i);
            int sqrtInt = (int)squareRoot;
            double decimalPart = squareRoot-sqrtInt;
            
            for(int j=1;j<=sqrtInt;j++){
                if(i%j==0){
                    divisor+=2;//약수는 쌍
                }
            }
            for(int j=2;j<squareRoot+1;j++){
                if(i%j==0){
                    divisor += 2;//약수는 반드시 쌍임
                }
            }            
            if(decimalPart==0.0){
                divisor--;
            }
            if(divisor>limit){
                divisor = power;//제한 넘으면 초과기사공격력
            }
            answer += divisor;
        }
        return answer;
    }
}

for(int j=1;j<=sqrtInt;j++){

범위 이렇게 바꿔봄

💡제곱근 할때 뭔가 sqrt+1보다 ≤ sqrt가 정확하지 않을까 했는데 맞았다!

⚠️약수 구할때 제곱근까지만 for문 돌리고 싶으면 범위가 ≤sqrt여야 함 <sqrt+1 말고

와 통과됐음!!!

제출 코드

import java.lang.Math;
class Solution {
    public int solution(int number, int limit, int power) {
        int answer = 0;
        for(int i = 1; i<=number ; i++){
            
            int divisor = 2;//자기 자신과 1은 반드시 약수니까
            double squareRoot = Math.sqrt(i);
            int sqrtInt = (int)squareRoot;
            double decimalPart = squareRoot-sqrtInt;
            
            for(int j=2;j<=sqrtInt;j++){
                if(i%j==0){
                    divisor+=2;//약수는 쌍
                }
            }           
            if(decimalPart==0.0){
                divisor--;
            }
            if(divisor>limit){
                divisor = power;//제한 넘으면 초과기사공격력
            }
            answer += divisor;
        }
        return answer;
    }
}

'Coding Test' 카테고리의 다른 글

[프로그래머스]과일장수  (1) 2023.11.30
[프로그래머스]명예의 전당  (1) 2023.11.30
[프로그래머스]소수 만들기  (2) 2023.11.29
[프로그래머스]모의고사 완전탐색  (1) 2023.11.29
[프로그래머스]2016년  (1) 2023.11.29