[문제 설명]

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

입출력 예

lineresult

[[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인 경우 두 직선은 평행 또는 일치합니다.

 


>문제 풀이

* 프로그래머스 위클리 챌린지 10주차 문제입니다.

 

문제에서 두 직선의 교점을 구하는 식을 줬습니다.

x= (BF-ED)/(AD-BC)

y= (EC-AF)/(AD-BC)

쉬운 증명이지만, 문제 풀이 전에 간단히 설명하고 넘어가겠습니다.

① Ax+By+E=0

② Cx+Dy+F=0

-----------------------

① x= -(By+E)/A

② x= -(Dy+F)/C

1, 2번 식을 통해서 (By+E)*C=(Dy+F)*A

(BC-AD)*y = (AF-CE)

y=(AF-CE)/(BC-AD)

같은 방법으로 x를 구할 수 있습니다.


 

자 이제 풀이에 관해,

1) for 문을 돌면서 두 직선들의 교점을 구하고, 구한 교점들을 hashset에 넣어줍니다.

1-1) (AD-BC)==0 이면, 두 직선은 평행하다고 볼 수 있습니다.

(만약, 분자가 0일 경우 두 직선이 겹친다는 소리인데, 문제에서 이런 경우의 입력은 안주어진다고 했으니 신경 안써줘도 됩니다.)

1-2) 두 교점이 정수인지 아닌지 판별하여, 구한 교점 x, y가 모두 정수인 것만 HashSet<Point> 에 넣어줍니다.

1-3) 구한 교점으로 x의 최댓값과, 최솟값, y의 최댓값과 최솟값을 구합니다. (=갱신해나갑니다.)

2) 구한 maxx, maxy, minx, miny로 구한 교점들을 넣을 판의 height와 width를 구합니다.

3) answer= new String[height] 로 판을 만들어주고, 일단 점으로 채워줍니다. ex) "........~........"

4) set에 넣어뒀던 교점들을 순회하며 answer판에 "*" 별을 넣어줍니다.

 

 

 

>전체 코드

 

import java.util.HashSet;
import java.util.Arrays;
class Solution {
    static long minx= Long.MAX_VALUE, miny= Long.MAX_VALUE;
    static long maxx= Long.MIN_VALUE, maxy= Long.MIN_VALUE;
    
    public String[] solution(int[][] line) {
        String[] answer = {};
        HashSet<Point> pSet= new HashSet<>();
        
        //x= (bf-ed)/(ad-bc)
        //y= (ec-af)/(ad-bc)
        long x, y;
        for(int i=0; i<line.length-1; i++){
            long a= line[i][0];
            long b= line[i][1];
            long e= line[i][2];
            for(int j=i+1; j<line.length; j++){
                long c= line[j][0];
                long d= line[j][1];
                long f= line[j][2];
                
                long adbc= a*d-b*c;
                if(adbc==0) continue; //비교대상 직선과 평행함
                
                long bfed= b*f-e*d;
                if(bfed%adbc!=0) continue; //x가 정수가 아님
                
                long ecaf= e*c-a*f;
                if(ecaf%adbc!=0) continue; //y가 정수가 아님
                
                x= bfed/adbc;
                y= ecaf/adbc;
                pSet.add(new Point(x, y));
                
                minx= Math.min(minx, x);
                miny= Math.min(miny, y);
                maxx= Math.max(maxx, x);
                maxy= Math.max(maxy, y);
            }
        }
        
        long height= maxy-miny+1;
        long width= maxx-minx+1;
        answer= new String[(int)height];
        StringBuilder sb= new StringBuilder();
        for(int i=0; i<width; i++){
            sb.append(".");
        }
        
        Arrays.fill(answer, sb.toString());
        
        long nx, ny;
        for(Point p: pSet){
            ny= maxy-p.y;
            nx= p.x-minx;
            answer[(int)ny]= answer[(int)ny].substring(0, (int)nx)+"*"
                        +answer[(int)ny].substring((int)nx+1);
        }
        
        return answer;
    }
    
    public class Point{
        long x;
        long y;
        public Point(long x, long y){
            this.x= x;
            this.y= y;
        }
    }
}

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

 

코딩테스트 연습 - 10주차_교점에 별 만들기

[[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

 

[문제]

n개의 송전탑이 전선을 통해 하나의 트리 형태로 연결되어 있습니다. 당신은 이 전선들 중 하나를 끊어서 현재의 전력망 네트워크를 2개로 분할하려고 합니다. 이때, 두 전력망이 갖게 되는 송전탑의 개수를 최대한 비슷하게 맞추고자 합니다.

송전탑의 개수 n, 그리고 전선 정보 wires가 매개변수로 주어집니다. 전선들 중 하나를 끊어서 송전탑 개수가 가능한 비슷하도록 두 전력망으로 나누었을 때, 두 전력망이 가지고 있는 송전탑 개수의 차이(절대값)를 return 하도록 solution 함수를 완성해주세요.

제한사항

n은 2 이상 100 이하인 자연수입니다.

wires는 길이가 n-1인 정수형 2차원 배열입니다.

wires의 각 원소는 [v1, v2] 2개의 자연수로 이루어져 있으며, 이는 전력망의 v1번 송전탑과 v2번 송전탑이 전선으로 연결되어 있다는 것을 의미합니다.

1 ≤ v1 < v2 ≤ n 입니다.

전력망 네트워크가 하나의 트리 형태가 아닌 경우는 입력으로 주어지지 않습니다.

 

입출력 예

n wires result
9 [[1,3],[2,3],[3,4],[4,5],[4,6],[4,7],[7,8],[7,9]] 3
4 [[1,2],[2,3],[3,4]] 0
7 [[1,2],[2,7],[3,7],[3,4],[4,5],[6,7]] 1

입출력 예 설명

입출력 예 1

다음 그림은 주어진 입력을 해결하는 방법 중 하나를 나타낸 것입니다.

4번과 7번을 연결하는 전선을 끊으면 두 전력망은 각 6개와 3개의 송전탑을 가지며, 이보다 더 비슷한 개수로 전력망을 나눌 수 없습니다.

또 다른 방법으로는 3번과 4번을 연결하는 전선을 끊어도 최선의 정답을 도출할 수 있습니다.

입출력 예 2

다음 그림은 주어진 입력을 해결하는 방법을 나타낸 것입니다.

2번과 3번을 연결하는 전선을 끊으면 두 전력망이 모두 2개의 송전탑을 가지게 되며, 이 방법이 최선입니다.

입출력 예 3

다음 그림은 주어진 입력을 해결하는 방법을 나타낸 것입니다.

3번과 7번을 연결하는 전선을 끊으면 두 전력망이 각각 4개와 3개의 송전탑을 가지게 되며, 이 방법이 최선입니다.

 


>문제 풀이

1. 인접 행렬 또는 인접리스트를 이용해서 그래프를 표현해줍니다.

2. for(반복문) : 선을 하나씩 끊어보며, 나눠진 두 전력망의 송전탑 개수의 차이를 구할 것입니다.

2-1) 선 하나를 끊습니다.

2-2) bfs 를 이용하여 끊어진 선의 두 노드 중 하나를 선택하여 연결된 송전탑의 개수를 구해줍니다.(=cnt)

- 두 전력망은 각각 (cnt) 와 ( n-cnt )개의 송전탑을 가질 것입니다.

- Math.abs(n-2*cnt) : 두 전력망이 가지고 있는 송전탑의 개수의 차

- 최솟값을 answer에 갱신해줍니다.

2-3) 선을 다시 복구시켜줍니다.

 

 

>전체 코드

 

import java.util.LinkedList;
import java.util.Queue;
class Solution {
    static int[][] arr;
    public int solution(int n, int[][] wires) {
        int answer = n;
        arr= new int[n+1][n+1];
        
        //1. 인접행렬에 input
        for(int i=0; i<wires.length; i++){
            arr[wires[i][0]][wires[i][1]]=1;
            arr[wires[i][1]][wires[i][0]]=1;
        }
        
        //2. 선을 하나씩 끊어보며 순회
        int a, b;
        for(int i=0; i<wires.length; i++){
            a= wires[i][0];
            b= wires[i][1];
            
            //선을 하나 끊고
            arr[a][b]=0;
            arr[b][a]=0;
            
            //bfs
            answer= Math.min(answer, bfs(n, a));
            
            //선 다시 복구
            arr[a][b]=1;
            arr[b][a]=1;
        }
        
        return answer;
    }
    
    public int bfs(int n, int start){
        int[] visit= new int[n+1];
        int cnt=1;
        
        Queue<Integer> queue= new LinkedList<>();
        queue.offer(start);
        
        while(!queue.isEmpty()){
            int point= queue.poll();
            visit[point]= 1;
            
            for(int i=1; i<=n; i++){ //point와 연결된 애들 중에 방문한적 없는 노드 전부 큐에 넣기
                if(visit[i]==1) continue;
                if(arr[point][i]==1) {
                    queue.offer(i);
                    cnt++;
                }
            }
        }
        return (int)Math.abs(n-2*cnt); //cnt-(n-cnt);
    }//bfs
}

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

 

코딩테스트 연습 - 9주차_전력망을 둘로 나누기

9 [[1,3],[2,3],[3,4],[4,5],[4,6],[4,7],[7,8],[7,9]] 3 7 [[1,2],[2,7],[3,7],[3,4],[4,5],[6,7]] 1

programmers.co.kr

 

[문제]

명함 지갑을 만드는 회사에서 지갑의 크기를 정하려고 합니다. 다양한 모양과 크기의 명함들을 모두 수납할 수 있으면서, 작아서 들고 다니기 편한 지갑을 만들어야 합니다. 이러한 요건을 만족하는 지갑을 만들기 위해 디자인팀은 모든 명함의 가로 길이와 세로 길이를 조사했습니다.

아래 표는 4가지 명함의 가로 길이와 세로 길이를 나타냅니다.

명함 번호 가로 길이 세로 길이
1 60 50
2 30 70
3 60 30
4 80 40

가장 긴 가로 길이와 세로 길이가 각각 80, 70이기 때문에 80(가로) x 70(세로) 크기의 지갑을 만들면 모든 명함들을 수납할 수 있습니다. 하지만 2번 명함을 가로로 눕혀 수납한다면 80(가로) x 50(세로) 크기의 지갑으로 모든 명함들을 수납할 수 있습니다. 이때의 지갑 크기는 4000(=80 x 50)입니다.

모든 명함의 가로 길이와 세로 길이를 나타내는 2차원 배열 sizes가 매개변수로 주어집니다. 모든 명함을 수납할 수 있는 가장 작은 지갑을 만들 때, 지갑의 크기를 return 하도록 solution 함수를 완성해주세요.

제한사항

sizes의 길이는 1 이상 10,000 이하입니다.

sizes의 원소는 [w, h] 형식입니다.

w는 명함의 가로 길이를 나타냅니다.

h는 명함의 세로 길이를 나타냅니다.

w와 h는 1 이상 1,000 이하인 자연수입니다.

입출력 예

sizes result
[[60, 50], [30, 70], [60, 30], [80, 40]] 4000
[[10, 7], [12, 3], [8, 15], [14, 7], [5, 15]] 120
[[14, 4], [19, 6], [6, 16], [18, 7], [7, 11]] 133

입출력 예 설명

입출력 예 1

문제 예시와 같습니다.

입출력 예 2

명함들을 적절히 회전시켜 겹쳤을 때, 3번째 명함(가로: 8, 세로: 15)이 다른 모든 명함보다 크기가 큽니다. 따라서 지갑의 크기는 3번째 명함의 크기와 같으며, 120(=8 x 15)을 return 합니다.

입출력 예 3

명함들을 적절히 회전시켜 겹쳤을 때, 모든 명함을 포함하는 가장 작은 지갑의 크기는 133(=19 x 7)입니다.

 


>문제 풀이

입력되는 값이 (a, b) 형태로 들어오는데, a와 b중 더 긴 길이를 가로로 놓고, 가로와 세로 각각 최댓값을 구해줍니다.

answer= maxx*maxy;

 

>전체 코드

 

class Solution {
    public int solution(int[][] sizes) {
        int answer = 0;
        int maxx= 0, maxy= 0;
        
        for(int i=0; i<sizes.length; i++){
            maxx= Math.max(maxx, Math.max(sizes[i][0], sizes[i][1]));
            maxy= Math.max(maxy, Math.min(sizes[i][0], sizes[i][1]));
        }
        answer= maxx*maxy;
        
        return answer;
    }
}

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

 

코딩테스트 연습 - 8주차_최소직사각형

[[10, 7], [12, 3], [8, 15], [14, 7], [5, 15]] 120 [[14, 4], [19, 6], [6, 16], [18, 7], [7, 11]] 133

programmers.co.kr

 

[문제]

카카오TV에서 유명한 크리에이터로 활동 중인 죠르디는 환경 단체로부터 자신의 가장 인기있는 동영상에 지구온난화의 심각성을 알리기 위한 공익광고를 넣어 달라는 요청을 받았습니다. 평소에 환경 문제에 관심을 가지고 있던 "죠르디"는 요청을 받아들였고 광고효과를 높이기 위해 시청자들이 가장 많이 보는 구간에 공익광고를 넣으려고 합니다. "죠르디"는 시청자들이 해당 동영상의 어떤 구간을 재생했는 지 알 수 있는 재생구간 기록을 구했고, 해당 기록을 바탕으로 공익광고가 삽입될 최적의 위치를 고를 수 있었습니다.

참고로 광고는 재생 중인 동영상의 오른쪽 아래에서 원래 영상과 동시에 재생되는 PIP(Picture in Picture) 형태로 제공됩니다.

다음은 "죠르디"가 공익광고가 삽입될 최적의 위치를 고르는 과정을 그림으로 설명한 것입니다.

그림의 파란색 선은 광고를 검토 중인 "죠르디" 동영상의 전체 재생 구간을 나타냅니다.

위 그림에서, "죠르디" 동영상의 총 재생시간은 02시간 03분 55초 입니다.

그림의 검은색 선들은 각 시청자들이 "죠르디"의 동영상을 재생한 구간의 위치를 표시하고 있습니다.

검은색 선의 가운데 숫자는 각 재생 기록을 구분하는 ID를 나타냅니다.

검은색 선에 표기된 왼쪽 끝 숫자와 오른쪽 끝 숫자는 시청자들이 재생한 동영상 구간의 시작 시각과 종료 시각을 나타냅니다.

위 그림에서, 3번 재생 기록은 00시 25분 50초 부터 00시 48분 29초 까지 총 00시간 22분 39초 동안 죠르디의 동영상을 재생했습니다. 1

위 그림에서, 1번 재생 기록은 01시 20분 15초 부터 01시 45분 14초 까지 총 00시간 24분 59초 동안 죠르디의 동영상을 재생했습니다.

그림의 빨간색 선은 "죠르디"가 선택한 최적의 공익광고 위치를 나타냅니다.

만약 공익광고의 재생시간이 00시간 14분 15초라면, 위의 그림처럼 01시 30분 59초 부터 01시 45분 14초 까지 공익광고를 삽입하는 것이 가장 좋습니다. 이 구간을 시청한 시청자들의 누적 재생시간이 가장 크기 때문입니다.

01시 30분 59초 부터 01시 45분 14초 까지의 누적 재생시간은 다음과 같이 계산됩니다.

01시 30분 59초 부터 01시 37분 44초 까지 : 4번, 1번 재생 기록이 두차례 있으므로 재생시간의 합은 00시간 06분 45초 X 2 = 00시간 13분 30초

01시 37분 44초 부터 01시 45분 14초 까지 : 4번, 1번, 5번 재생 기록이 세차례 있으므로 재생시간의 합은 00시간 07분 30초 X 3 = 00시간 22분 30초

따라서, 이 구간 시청자들의 누적 재생시간은 00시간 13분 30초 + 00시간 22분 30초 = 00시간 36분 00초입니다.

[문제]

"죠르디"의 동영상 재생시간 길이 play_time, 공익광고의 재생시간 길이 adv_time, 시청자들이 해당 동영상을 재생했던 구간 정보 logs가 매개변수로 주어질 때, 시청자들의 누적 재생시간이 가장 많이 나오는 곳에 공익광고를 삽입하려고 합니다. 이때, 공익광고가 들어갈 시작 시각을 구해서 return 하도록 solution 함수를 완성해주세요. 만약, 시청자들의 누적 재생시간이 가장 많은 곳이 여러 곳이라면, 그 중에서 가장 빠른 시작 시각을 return 하도록 합니다.

[제한사항]

play_time, adv_time은 길이 8로 고정된 문자열입니다.

play_time, adv_time은 HH:MM:SS 형식이며, 00:00:01 이상 99:59:59 이하입니다.

즉, 동영상 재생시간과 공익광고 재생시간은 00시간 00분 01초 이상 99시간 59분 59초 이하입니다.

공익광고 재생시간은 동영상 재생시간보다 짧거나 같게 주어집니다.

logs는 크기가 1 이상 300,000 이하인 문자열 배열입니다.

logs 배열의 각 원소는 시청자의 재생 구간을 나타냅니다.

logs 배열의 각 원소는 길이가 17로 고정된 문자열입니다.

logs 배열의 각 원소는 H1:M1:S1-H2:M2:S2 형식입니다.

H1:M1:S1은 동영상이 시작된 시각, H2:M2:S2는 동영상이 종료된 시각을 나타냅니다.

H1:M1:S1H2:M2:S2보다 1초 이상 이전 시각으로 주어집니다.

H1:M1:S1H2:M2:S2는 play_time 이내의 시각입니다.

시간을 나타내는 HH, H1, H2의 범위는 00~99, 분을 나타내는 MM, M1, M2의 범위는 00~59, 초를 나타내는 SS, S1, S2의 범위는 00~59까지 사용됩니다. 잘못된 시각은 입력으로 주어지지 않습니다. (예: 04:60:24, 11:12:78, 123:12:45 등)

return 값의 형식

공익광고를 삽입할 시각을 HH:MM:SS 형식의 8자리 문자열로 반환합니다.

[입출력 예]

play_time adv_time logs result
"02:03:55" "00:14:15" ["01:20:15-01:45:14", "00:40:31-01:00:00", "00:25:50-00:48:29", "01:30:59-01:53:29", "01:37:44-02:02:30"] "01:30:59"
"99:59:59" "25:00:00" ["69:59:59-89:59:59", "01:00:00-21:00:00", "79:59:59-99:59:59", "11:00:00-31:00:00"] "01:00:00"
"50:00:00" "50:00:00" ["15:36:51-38:21:49", "10:14:18-15:36:51", "38:21:49-42:51:45"] "00:00:00"

입출력 예에 대한 설명

입출력 예 1

문제 예시와 같습니다.

입출력 예 2

01:00:00에 공익광고를 삽입하면 26:00:00까지 재생되며, 이곳이 가장 좋은 위치입니다. 이 구간의 시청자 누적 재생시간은 다음과 같습니다.

01:00:00-11:00:00 : 해당 구간이 1회(2번 기록) 재생되었으므로 누적 재생시간은 10시간 00분 00초 입니다.

11:00:00-21:00:00 : 해당 구간이 2회(2번, 4번 기록) 재생되었으므로 누적 재생시간은 20시간 00분 00초 입니다.

21:00:00-26:00:00 : 해당 구간이 1회(4번 기록) 재생되었으므로 누적 재생시간은 05시간 00분 00초 입니다.

따라서, 이 구간의 시청자 누적 재생시간은 10시간 00분 00초 + 20시간 00분 00초 + 05시간 00분 00초 = 35시간 00분 00초 입니다.

초록색으로 표시된 구간(69:59:59-94:59:59)에 광고를 삽입해도 동일한 결과를 얻을 수 있으나, 01:00:0069:59:59 보다 빠른 시각이므로, "01:00:00"을 return 합니다.

입출력 예 3

동영상 재생시간과 공익광고 재생시간이 같으므로, 삽입할 수 있는 위치는 맨 처음(00:00:00)이 유일합니다.

동영상 재생시간 = 재생이 종료된 시각 - 재생이 시작된 시각(예를 들어, 00시 00분 01초부터 00시 00분 10초까지 동영상이 재생되었다면, 동영상 재생시간은 9초 입니다.)

 


>문제 풀이

죠르디의 동영상 재생시간 길이: play_time

공익광고의 재생시간 길이: adv_time

시청자들이 해당 동영상을 재생했던 구간 정보: logs[]



가장 많이 누적된 재생 시간에 광고를 삽입해야합니다.

그럴러면 가장 많이 누적되는 시간의 구간을 알아내야하고, 이를 위해서는 초당 누적량을 알아햐 합니다.

즉, 초당 누적량을 가지고 구한_ 누적합의 max값을 찾는 방향으로 광고 삽입 시간을 구해야합니다.

설명을 좀 더 구체적으로 풀어보자면,

배열 sum[ play_time+1 ]을 만들어 입력되는 logs 배열에 따라 시작시간부터 종료시간까지 1초마다 누적시킵니다.(sum[i]++ : 1초 사이에 몇 명의 사용자가 영상을 재생했는지 저장함으로써 초당 누적사용량을 구합니다.)

그렇게 구한 배열값으로 i초부터 j초까지의 누적 재생시간을 구할 수 있습니다.

기본적으로는 sum[0]~sum[adv_time-1] : 0초부터 광고시간까지의 누적 재생량을 구할 수 있습니다.

이제 1초씩 영상의 뒤로 이동하며, 광고시간 길이만큼의 누적 재생시간의 최댓값을 갱신합니다.

구해야할 값은 광고를 삽입할 시간이므로 answer에는 구한 누적 재생시간의 시작시간을 같이 갱신시켜줘야합니다.

1. 먼저 전체 동영상 길이의 시간만큼의 배열을 만듭니다. int sum[playtime+1]

2. 입력된 logs[] 배열의 시작시간과 종료시간에 따라 sum[i]++; 누적해줍니다.

※ 예제를 보면 시간 구할 때 '종료시간 - 시작시간' 입니다. 즉 종료시간은 sum[종료시간]++ 해주면 안됩니다. 저는 처음에 풀 때, 이 부분을 놓쳤었어요..ㅎ;

3. 그리고 광고시간만큼의 구간을 +1씩 이동해가면서 구간별 최대 누적합을 찾으면서 answer값을(광고 삽입시간) 갱신해줍니다.

+)문자열 포맷 ↔ 초단위 시간 계산을 할 함수 2개가 필요합니다. String to Int 와 Int to String

입력은 String "HH:MM:SS" 형태로 들어오는데, 계산은 int로 해야되고 출력은 다시 "HH:MM:SS"로 해야합니다. ( 시간은 초를 기준으로 합니다.)

 

 

>전체 코드

 

class Solution {
    public String solution(String play_time, String adv_time, String[] logs) {
        int answer = 0;
        int playtime= strToInt(play_time);
        int advtime= strToInt(adv_time);
        int[] sum= new int[playtime+1];
        
        for(String str: logs){
            String[] arr= str.split("-");
            int start= strToInt(arr[0]);
            int end= strToInt(arr[1]);
            
            for(int i=start; i<end; i++){
                sum[i]++;
            }
        }
        
        long max=0;
        for(int i=1; i<advtime; i++){
            max+= sum[i];
        }
        
        long now= max;
        for(int i=0, j=advtime; j<playtime; i++, j++){
            now= now-sum[i]+sum[j];
            if(now>max){
                answer=i+1;
                max= now;
            }
        }
        
        return intToStr(answer);
    }
    
    //str to int
    public int strToInt(String time_str){
        //HH:MM:SS 형태로 들어오게됨
        String[] arr= time_str.split(":");
        int time= Integer.parseInt(arr[0])*3600
                +Integer.parseInt(arr[1])*60+Integer.parseInt(arr[2]);
        return time;
    }
    
    //int to str
    public String intToStr(int time){
        String hh, mm, ss;
        hh= (time/3600)>9? (time/3600)+"":"0"+time/3600;
        time%=3600;
        mm= (time/60)>9? (time/60)+"":"0"+time/60;
        time%=60;
        ss= time>9? time+"":"0"+time;
        
        return hh+":"+mm+":"+ss;
    }
}

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

 

코딩테스트 연습 - 광고 삽입

시간을 나타내는 HH, H1, H2의 범위는 00~99, 분을 나타내는 MM, M1, M2의 범위는 00~59, 초를 나타내는 SS, S1, S2의 범위는 00~59까지 사용됩니다. 잘못된 시각은 입력으로 주어지지 않습니다. (예: 04:60:24, 11

programmers.co.kr

 

[문제]

* 본 문제는 정확성과 효율성 테스트 각각 점수가 있는 문제입니다.

카카오는 하반기 경력 개발자 공개채용을 진행 중에 있으며 현재 지원서 접수와 코딩테스트가 종료되었습니다. 이번 채용에서 지원자는 지원서 작성 시 아래와 같이 4가지 항목을 반드시 선택하도록 하였습니다.

- 코딩테스트 참여 개발언어 항목에 cpp, java, python 중 하나를 선택해야 합니다.

- 지원 직군 항목에 backend와 frontend 중 하나를 선택해야 합니다.

- 지원 경력구분 항목에 junior와 senior 중 하나를 선택해야 합니다.

- 선호하는 소울푸드로 chicken과 pizza 중 하나를 선택해야 합니다.

인재영입팀에 근무하고 있는 니니즈는 코딩테스트 결과를 분석하여 채용에 참여한 개발팀들에 제공하기 위해 지원자들의 지원 조건을 선택하면 해당 조건에 맞는 지원자가 몇 명인 지 쉽게 알 수 있는 도구를 만들고 있습니다.

예를 들어, 개발팀에서 궁금해하는 문의사항은 다음과 같은 형태가 될 수 있습니다.

코딩테스트에 java로 참여했으며, backend 직군을 선택했고, junior 경력이면서, 소울푸드로 pizza를 선택한 사람 중 코딩테스트 점수를 50점 이상 받은 지원자는 몇 명인가?

물론 이 외에도 각 개발팀의 상황에 따라 아래와 같이 다양한 형태의 문의가 있을 수 있습니다.

- 코딩테스트에 python으로 참여했으며, frontend 직군을 선택했고, senior 경력이면서, 소울푸드로 chicken을 선택한 사람 중 코딩테스트 점수를 100점 이상 받은 사람은 모두 몇 명인가?

- 코딩테스트에 cpp로 참여했으며, senior 경력이면서, 소울푸드로 pizza를 선택한 사람 중 코딩테스트 점수를 100점 이상 받은 사람은 모두 몇 명인가?

- backend 직군을 선택했고, senior 경력이면서 코딩테스트 점수를 200점 이상 받은 사람은 모두 몇 명인가?

- 소울푸드로 chicken을 선택한 사람 중 코딩테스트 점수를 250점 이상 받은 사람은 모두 몇 명인가?

- 코딩테스트 점수를 150점 이상 받은 사람은 모두 몇 명인가?

즉, 개발팀에서 궁금해하는 내용은 다음과 같은 형태를 갖습니다.

[조건]을 만족하는 사람 중 코딩테스트 점수를 X점 이상 받은 사람은 모두 몇 명인가?

[문제]

지원자가 지원서에 입력한 4가지의 정보와 획득한 코딩테스트 점수를 하나의 문자열로 구성한 값의 배열 info, 개발팀이 궁금해하는 문의조건이 문자열 형태로 담긴 배열 query가 매개변수로 주어질 때,

각 문의조건에 해당하는 사람들의 숫자를 순서대로 배열에 담아 return 하도록 solution 함수를 완성해 주세요.

[제한사항]

- info 배열의 크기는 1 이상 50,000 이하입니다.

- info 배열 각 원소의 값은 지원자가 지원서에 입력한 4가지 값과 코딩테스트 점수를 합친 "개발언어 직군 경력 소울푸드 점수" 형식입니다.

> 개발언어는 cpp, java, python 중 하나입니다.

> 직군은 backend, frontend 중 하나입니다.

> 경력은 junior, senior 중 하나입니다.

> 소울푸드는 chicken, pizza 중 하나입니다.

> 점수는 코딩테스트 점수를 의미하며, 1 이상 100,000 이하인 자연수입니다.

> 각 단어는 공백문자(스페이스 바) 하나로 구분되어 있습니다.

- query 배열의 크기는 1 이상 100,000 이하입니다.

- query의 각 문자열은 "[조건] X" 형식입니다.

[조건]은 "개발언어 and 직군 and 경력 and 소울푸드" 형식의 문자열입니다.

> 언어는 cpp, java, python, - 중 하나입니다.

> 직군은 backend, frontend, - 중 하나입니다.

> 경력은 junior, senior, - 중 하나입니다.

> 소울푸드는 chicken, pizza, - 중 하나입니다.

> '-' 표시는 해당 조건을 고려하지 않겠다는 의미입니다.

> X는 코딩테스트 점수를 의미하며 조건을 만족하는 사람 중 X점 이상 받은 사람은 모두 몇 명인 지를 의미합니다.

> 각 단어는 공백문자(스페이스 바) 하나로 구분되어 있습니다.

> 예를 들면, "cpp and - and senior and pizza 500"은 "cpp로 코딩테스트를 봤으며, 경력은 senior 이면서 소울푸드로 pizza를 선택한 지원자 중 코딩테스트 점수를 500점 이상 받은 사람은 모두 몇 명인가?"를 의미합니다.

[입출력 예]

info query result
["java backend junior pizza 150","python frontend senior chicken 210","python frontend senior chicken 150","cpp backend senior pizza 260","java backend junior chicken 80","python backend senior chicken 50"] ["java and backend and junior and pizza 100","python and frontend and senior and chicken 200","cpp and - and senior and pizza 250","- and backend and senior and - 150","- and - and - and chicken 100","- and - and - and - 150"] [1,1,1,1,2,4]

 

입출력 예에 대한 설명

지원자 정보를 표로 나타내면 다음과 같습니다.

언어 직군 경력 소울 푸드 점수
java backend junior pizza 150
python frontend senior chicken 210
python frontend senior chicken 150
cpp backend senior pizza 260
java backend junior chicken 80
python backend senior chicken 50

"java and backend and junior and pizza 100" : java로 코딩테스트를 봤으며, backend 직군을 선택했고 junior 경력이면서 소울푸드로 pizza를 선택한 지원자 중 코딩테스트 점수를 100점 이상 받은 지원자는 1명 입니다.

"python and frontend and senior and chicken 200" : python으로 코딩테스트를 봤으며, frontend 직군을 선택했고, senior 경력이면서 소울 푸드로 chicken을 선택한 지원자 중 코딩테스트 점수를 200점 이상 받은 지원자는 1명 입니다.

"cpp and - and senior and pizza 250" : cpp로 코딩테스트를 봤으며, senior 경력이면서 소울푸드로 pizza를 선택한 지원자 중 코딩테스트 점수를 250점 이상 받은 지원자는 1명 입니다.

"- and backend and senior and - 150" : backend 직군을 선택했고, senior 경력인 지원자 중 코딩테스트 점수를 150점 이상 받은 지원자는 1명 입니다.

"- and - and - and chicken 100" : 소울푸드로 chicken을 선택한 지원자 중 코딩테스트 점수를 100점 이상을 받은 지원자는 2명 입니다.

"- and - and - and - 150" : 코딩테스트 점수를 150점 이상 받은 지원자는 4명 입니다.

 


>문제 풀이

1.) 입력된 info를 띄어쓰기로 쪼개서 arr배열을 만든다.

ex) "java backend junior pizza 150" info에 대해

=> arr[]={"java", "backend", "junior", "pizza", "150"};

1-1) combination(String str, int length, arr)

: 함수를 이용하여 arr로 만들 수 있는 모든 조합을 map에 넣는다.

map은 hashmap<String, ArrayList<Integer>> 구조로 Key값에는 조합으로 만들어진 문자열을 넣고, Value값에는 문자열에 따라 점수를 리스트에 추가하는 방법으로 저장한다.

    //조합 (str, cond조건, arr[])
    //info로 만들 수 있는 모든 조합을 만들고 str마다 점수를 리스트로 만들어서 map에 저장
    public void combination(String str, int length, String[] arr){
        if(length==4){
            int score= Integer.parseInt(arr[4]);
            if(map.containsKey(str)) map.get(str).add(score);
            else{
                ArrayList<Integer> list= new ArrayList<>();
                list.add(score);
                map.put(str, list);
            }
            return;
        }
        combination(str+"-", length+1, arr);
        combination(str+arr[length], length+1, arr);
    }

2.) map에 있는 점수리스트 전부 오름차순 정렬해주기

** 이부분을 놓쳐서 효율성 검사에서 시간초과가 났었다.

입력 제한사항으로 info는 5만개, query는 10만개까지 들어올 수 있다.

조건은 (cpp/java/python/ - ) x (backend/frontend/ - ) x (junior/senior/ - ) x (chicken/pizza/ - )

따라서 만들 수 있는 조합의 최대 개수라고 해봐야 4*3*3*3= 108개 밖에 안된다.

근데 query를 순회하면서 정렬을 해줄 경우 최대 10만번이나 하는 상황이 생길 수 있다.

3) query를 조건문자열과 점수로 쪼개서 원하는 [조건]에 점수리스트를 이분탐색하여 원하는 점수 이상의 지원자의 인원을 파악한다.

ex) query[0] [조건]: "javabackendjuniorpizza" / 점수: 100점

map에 key값이 "javabackendjuniorpizza"인 value 즉 점수 리스트를 이분 탐색한다.

 

 

 

>전체 코드

 

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Collections;
class Solution {
    static HashMap<String, ArrayList<Integer>> map;
    public int[] solution(String[] info, String[] query) {
        int[] answer = new int[query.length];
        map= new HashMap<>();
        
        //1. info는 " "띄어쓰기로 쪼갤 수 있음
        // 쪼개서 combination()으로 만들 수 있는 모든 조합을 map에 넣는다.
        for(String str: info){
            String arr[]= str.split(" ");
            combination("", 0, arr);
        }
        
        //2. map에 있는 점수 리스트: 오름차순 정렬
        for(String str: map.keySet()){
            Collections.sort(map.get(str));
        }
        
        //3. query를 and로 쪼개서 [조건 문자열]과 점수로 나누고
        // [조건]에 맞는 지원자 중에 점수 리스트를 이분탐색하여 답을 구한다.
        int ansIdx=0;
        for(String str: query){
            String new_str= str.replace(" and ", "");
            String[] q_arr= new_str.split(" ");
            
            answer[ansIdx++]= binarySearch(q_arr[0], Integer.parseInt(q_arr[1]));
        }
        
        return answer;
    }
    
    //조합 (str, cond조건, arr[])
    //info로 만들 수 있는 모든 조합을 만들고 str마다 점수를 리스트로 만들어서 map에 저장
    public void combination(String str, int length, String[] arr){
        if(length==4){
            int score= Integer.parseInt(arr[4]);
            if(map.containsKey(str)) map.get(str).add(score);
            else{
                ArrayList<Integer> list= new ArrayList<>();
                list.add(score);
                map.put(str, list);
            }
            return;
        }
        combination(str+"-", length+1, arr);
        combination(str+arr[length], length+1, arr);
    }
    
    //이분탐색: 원하는 점수 이상 받은 지원자를 찾기위해
    //같은 점수일 경우 가장 왼쪽에 있는 index 채택
    public int binarySearch(String query, int score){
        if(!map.containsKey(query)) return 0;
        
        ArrayList<Integer> list= map.get(query);
        int start=0, end=list.size()-1, mid=0;
        while(start<=end){
            mid= (start+end)/2;
            if(list.get(mid)<score){
                start= mid+1;
            }else{ //score<=mid
                end= mid-1;
            }
        }
        return list.size()-start;
    }//bn_Search
}

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

 

코딩테스트 연습 - 순위 검색

["java backend junior pizza 150","python frontend senior chicken 210","python frontend senior chicken 150","cpp backend senior pizza 260","java backend junior chicken 80","python backend senior chicken 50"] ["java and backend and junior and pizza 100","pyt

programmers.co.kr

 

+ Recent posts