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

[프로그래머스,Java] Level2:양궁대회

류창 2022. 2. 8. 20:38
반응형

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

 

코딩테스트 연습 - 양궁대회

문제 설명 카카오배 양궁대회가 열렸습니다. 라이언은 저번 카카오배 양궁대회 우승자이고 이번 대회에도 결승전까지 올라왔습니다. 결승전 상대는 어피치입니다. 카카오배 양궁대회 운영위원

programmers.co.kr

 

문제가 길어 링크로 대체합니다.

 

 

문제분석:

 

목표: 라이언과 어피치의 과녁점수가 최대일때 맞춘 과녁의 점수판을 반환해주세요.

 

 

특수조건:

1. 라이언과 어피치가 동점일경우, 어피치의 승리입니다.

2. 어피치가 승리할경우 -1을 반환

3. 만약, 과녁점수가 동일한기록이 여러개 존재할경우, 낮은 점수를 많이 맞힌것을 정답으로합니다.

 

 

이 문제가 정말 어렵다고 느끼게하는건 바로 3번때문일겁니다.

 

라이언 - 어피치 점수가 최대인 기록이 여러개있다면, 그중에서 낮은점수를 많이 맞힌걸 뽑아야합니다.

 

그래서 어쩔수없이 완전탐색을 돌려야합니다.

전 백트래킹을 사용하여 완전탐색을 돌렷습니다.

 

우선 전역변수입니다. 백트래킹을 깔끔하게 돌리기위해서 미리 선언해뒀습니다.

화살의 점수는 0~10점이니 11개의 배열을 만들어줍니다.

 

visited는  그 점수를 먹었는지 안먹었는지 확인하는용도

total은  어피치가 먼저 기록을낸 점수의 합계

max는   라이언 - 어피치의 최대 점수차

need 는  해당 점수를 먹기위해 화살을 몇발 투자해야하는지를 저장하는 배열입니다.

vict는  victory의 약자로,  최종적으로 반환할 답안입니다.

 

메인함수부분입니다. need[]의 배열을 채워주는데

 

need는 어피치가 투자한 화살 +1만큼 투자하면 그 점수를 얻을수 있다는 뜻입니다.

그리고 need[10]을 0으로 다시 설정한 이유는, 투자를하던 안하던 0점을 얻을수 있기때문입니다. 

 

그리고 rec() 백트래킹을 실행합니다.

 

이 결과로 max==0 즉, 어피치와 라이언이 동점일경우 -1을 반환합니다.

 

 

백트래킹 부분입니다. 

 

우선 화살을 먹을때의 조건을 n>=need[i] 즉,  X점 화살을 먹기위한 화살이 충분한가?를

따져줍니다.

 

그 후, 백트래킹의 기본인  visited 방문체크를합니다.

방문 체크를하였으니,  사용한 화살도 빼줍니다. n-=need[i]

 

그리고 rec(start+1,r-1,n) 으로 재귀를 돌립니다. 

 

재귀를 돌렸다면 방문체크를 해재하고 화살도 다시 더해줍니다.

 

 

그리고 아주 중요한 탈출조건입니다.

 

보통 탈출조건은 1개인데 이번 문제는 탈출조건이 2개정도 필요한것같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
if(n==0){
            int sum=0;
            int[] arr = new int[11];
            for(int i=0;i<11;i++){
                if(visited[i]){               
                    arr[i]=need[i];
                    if(arr[i]==1){
                        sum+=10-i;
                    }
                    else{
                        sum+=(10-i)*2;
                    }
                }              
            }
            if(max==sum-total){
                for(int i=10;i>=0;i--){
                    if(vict[i]>arr[i]){
                        break;
                    }
                    else if(vict[i]==arr[i]){
                        continue;
                    }
                    else{
                        vict=arr;
                    }
                }
            }
            else if(max<sum-total){
               max=Math.max(sum-total,max);
               vict=arr;
            }
            return;
        }
cs

첫번째 탈출조건입니다. n==0일때 즉, 화살을 모두 소진했을때입니다.

2~14줄은 얻은 라이언의 점수를 계산하는 로직

15~31줄은 라이언-어피치인 max를 갱신하는 로직입니다.

특히 if(max==sum-total)일때, 최고점수가 같을때  0점화살부터 비교하여 화살이 많은걸 우승후보로 설정합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
if(r==0){
            int sum=0;
            int[] arr = new int[11];
            for(int i=0;i<11;i++){
                if(visited[i]){               
                    arr[i]=need[i];
                    if(arr[i]==1){
                        sum+=10-i;
                    }
                    else{
                        sum+=(10-i)*2;
                    }
                }              
            }
            arr[10]=n;
            if(max==sum-total){
                for(int i=10;i>=0;i--){
                    if(vict[i]>arr[i]){
                        break;
                    }
                    else if(vict[i]==arr[i]){
                        continue;
                    }
                    else{
                        vict=arr;
                    }
                }
            }
            else if(max<sum-total){
               max=Math.max(sum-total,max);
               vict=arr;
            }
            return;
        }
cs

두번째 탈출조건입니다 r==0일때,  즉 화살이 남았을때(0점을 쏠때) 입니다.

0점을쏠때는 쏘든 안쏘든 가치가 0이기때문에 r==0일때만 기록이 잡힙니다.

 

앞에 n==0일때와 99프로 같은로직이지만 다른것이 15줄의 arr[10]=n; 부분입니다.

남은 화살 n을 0점에다 박아버리는것이죠.

 

생각해보니 이렇게 99프로 겹치면 모듈(메소드)화 한뒤, 깔끔하게 정리하는게 올바른 코딩인데

시험삼아 돌려본다는것이 기록이 박제가됨

 

이렇게 탈출조건 2개를 잡아놓으면 통과가됩니다.

문제풀이:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
class Solution {
 
boolean[] visited = new boolean[11];
int total=0;
int max=0;
int[] need= new int[11];
int[] vict= new int[11];
 
    public int[] solution(int n, int[] info) {
        int[] answer = {};
        for(int i=0;i<11;i++){
            need[i]=info[i]+1;
            if(info[i]!=0){
                total+=10-i;
            }
        }
        need[10]=0;
        rec(0,11,n);    
        if(max==0){
            return new int[]{-1};
        }
        return vict;
    }
 
    public void rec(int start,int r,int n){
        if(r==0){
            int sum=0;
            int[] arr = new int[11];
            for(int i=0;i<11;i++){
                if(visited[i]){               
                    arr[i]=need[i];
                    if(arr[i]==1){
                        sum+=10-i;
                    }
                    else{
                        sum+=(10-i)*2;
                    }
                }              
            }
            arr[10]=n;
            if(max==sum-total){
                for(int i=10;i>=0;i--){
                    if(vict[i]>arr[i]){
                        break;
                    }
                    else if(vict[i]==arr[i]){
                        continue;
                    }
                    else{
                        vict=arr;
                    }
                }
            }
            else if(max<sum-total){
               max=Math.max(sum-total,max);
               vict=arr;
            }
            return;
        }
        if(n==0){
            int sum=0;
            int[] arr = new int[11];
            for(int i=0;i<11;i++){
                if(visited[i]){               
                    arr[i]=need[i];
                    if(arr[i]==1){
                        sum+=10-i;
                    }
                    else{
                        sum+=(10-i)*2;
                    }
                }              
            }
            if(max==sum-total){
                for(int i=10;i>=0;i--){
                    if(vict[i]>arr[i]){
                        break;
                    }
                    else if(vict[i]==arr[i]){
                        continue;
                    }
                    else{
                        vict=arr;
                    }
                }
            }
            else if(max<sum-total){
               max=Math.max(sum-total,max);
               vict=arr;
            }
            return;
        }
        
        for(int i=start;i<11;i++){
            if(n>=need[i]){
            visited[i]=true;
            n-=need[i];
            rec(start+1,r-1,n);
            visited[i]=false;
            n+=need[i];
            }
 
        }   
    }
}
cs
반응형