문제 URL : https://school.programmers.co.kr/learn/courses/30/lessons/42885

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

문제 설명

사람들의 무게를 담은 배열과 구명보트 1개가 최대로 담을 수 있는 무게의 한계값(limit)이 주어짐

이를 토대로 필요한 구명보트 수의 최솟값을 구해야 함

단, 구명보트에는 최대 2명만이 탑승할 수 있음

 

풀이법 구상

무게 배열을 정렬하고, 가장 큰 값과 작은 값을 더해서 limit을 넘는지 안 넘는지를 토대로 개수를 구함

 

[큰 값과 작은 값을 더했을 때 limit 이하인 경우]

-> 두 사람(큰 값, 작은 값)이 보트 하나에 탔다고 치고, 큰/작은 값에 해당하는 인덱스를 한 칸씩 이동 + 새로운 구명보트 꺼냄(카운트 1 증가)

 

[큰 값과 작은 값을 더했을 때 limit 초과인 경우]

-> 한 사람(큰 값)만 보트에 태우고, 큰 값에 해당하는 인덱스를 한 칸 이동 + 새로운 구명보트 꺼냄(카운트 1 증가)

 

코드 구현 (Java)

 

import java.util.*;

class Solution {
    public int solution(int[] people, int limit) {
        Arrays.sort(people);
        int answer = 0;
        int idx = 0;
        for (int i=people.length-1; i>=idx; i--) {
            if (people[i]+people[idx] <= limit) idx++;
            answer++;
        }
        return answer;
    }
}

 

문제 URL : https://school.programmers.co.kr/learn/courses/30/lessons/138476

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

문제 설명

귤 크기를 의미하는 정수 배열이 주어짐

이 중 k개의 귤을 뽑아야 할 때, 크기의 종류 최솟값을 구해야 함

 

풀이법 구상

<틀린 풀이>

DFS를 활용하여 귤 크기 배열 내 부분 집합을 구하여 최솟값 갱신

-> n은 최대 100,000인데, dfs가 끝날 때마다 ch 배열을 전부 확인하게 되며 O(N^2)의 시간 복잡도를 가지게 됨

더보기

틀린 풀이의 코드 (Swift)

import Foundation

var answer = 100000
var ch:[Bool] = []

func solution(_ k:Int, _ tangerine:[Int]) -> Int {
    ch = Array(repeating: false, count: tangerine.count)
    dfs(0, tangerine, k)
    return answer
}

func dfs(_ v: Int, _ t:[Int], _ k: Int) {
    if(v == t.count) {
        var sizes = Set<Int>()
        var contain_cnt = 0
        for i in 0..<t.count {
            if(ch[i]) {
                contain_cnt += 1
                sizes.insert(t[i])
            }
        }
        if(!sizes.isEmpty && contain_cnt == k && answer > sizes.count) { 
            answer = sizes.count
        }
        
    } else {
        ch[v] = true
        dfs(v+1, t, k)
        ch[v] = false
        dfs(v+1, t, k)
    }
}

 

<정답 풀이>

딕셔너리를 통해 귤의 크기 별 개수를 정의

크기 별 개수를 기준으로 딕셔너리를 내림차순 정렬

딕셔너리를 돌며 개수와 종류 수를 누적합하며, 그 값이 k 이상이 될 경우 누적된 종류 수를 반환

(개수가 많은 것부터 탐색하므로 누적된 종류 수는 최소일 수밖에 없음)

 

 

코드 구현 (Swift)

import Foundation

func solution(_ k:Int, _ tangerine:[Int]) -> Int {
    var dict:[Int:Int] = [:]
    for t in tangerine {
        if(dict[t] == nil) { dict[t] = 1 }
        else { dict[t]! += 1 }
    }
    let s_dict = dict.sorted { $0.value > $1.value }
    
    var sum = 0
    var answer = 0
    for d in s_dict {
        sum += d.value
        answer += 1
        if(sum >= k) { return answer }
    }
    return answer
}

문제 URL : https://solvesql.com/problems/weekday-stats-airpollution/

 

https://solvesql.com/problems/weekday-stats-airpollution/

 

solvesql.com

 

*문제 저작권으로 인하여 직접 작성한 쿼리문만 첨부

 

select case strftime('%u',measured_at)
  when '1' then '월요일'
  when '2' then '화요일'
  when '3' then '수요일'
  when '4' then '목요일'
  when '5' then '금요일'
  when '6' then '토요일'
  when '7' then '일요일'
  end as weekday,
  round(avg(no2),4) as no2,
  round(avg(o3),4) as o3,
  round(avg(co),4) as co,
  round(avg(so2),4) as so2,
  round(avg(pm10),4) as pm10,
  round(avg(pm2_5),4) as pm2_5

from measurements
group by 1
order by strftime('%u',measured_at)

 

strftime을 통해 날짜 데이터를 기반으로 요일 값(1~7) 추출

case when문을 통해 특정 조건에 따른 새로운 열 생성

avg를 통해 평균값 도출

round을 통해 소수 반올림 수행

문제 URL : https://solvesql.com/problems/shoppingmall-monthly-summary/

 

https://solvesql.com/problems/shoppingmall-monthly-summary/

 

solvesql.com

 

*문제 저작권으로 인하여 직접 작성한 쿼리문만 첨부

 

select 
  strftime('%Y-%m',order_date) as order_month,
  sum(case when orders.order_id not like 'C%' then price*quantity else 0 end) as ordered_amount,
  sum(case when orders.order_id like 'C%' then price*quantity else 0 end) as canceled_amount,
  sum(price*quantity) as total_amount

from orders left join order_items
on orders.order_id = order_items.order_id

group by order_month
order by order_month

 

strftime(형식, 데이터)를 통해 특정 형태의 날짜 데이터 반환

case when 문을 통해 특정 조건에 해당하는 값만 추출하여 활용

left join ~ on 문을 통해 테이블 결합

문제 URL : https://solvesql.com/problems/mentor-mentee-list/

 

https://solvesql.com/problems/mentor-mentee-list/

 

solvesql.com

 

*문제 저작권으로 인하여 직접 작성한 쿼리문만 첨부

 

select YB.employee_id as mentee_id, 
       YB.name as mentee_name,
       OB.employee_id as mentor_id,
       OB.name as mentor_name

from employees YB cross join employees OB

where YB.join_date between '2021-09-01' and '2021-12-31'
and OB.join_date <= '2019-12-31'
and YB.department != OB.department

order by mentee_id, mentor_id

 

두 테이블 간의 결합에 있어 가능한 모든 조합을 확인할 수 있는 CROSS JOIN 사용

문제 URL : https://solvesql.com/problems/artists-without-artworks/

 

https://solvesql.com/problems/artists-without-artworks/

 

solvesql.com

 

*문제 저작권으로 인하여 직접 작성한 쿼리문만 첨부

 

select artist_id, name
from artists
where death_year is not null
and artist_id not in (
  select artist_id from artworks_artists
)

 

서브쿼리를 통한 조건 추가 사용

 

문제 URL

https://www.acmicpc.net/problem/2252

 

 

문제 설명 요약

  • 키가 작은 학생이 더 큰 학생보다 앞에 서도록 줄 세우기

 

문제 풀이

  • 위상정렬 사용
  • 키 비교 값을 방향을 가진 간선 정보로 사용
  • indegree의 값을 통해 우선적으로 위치해야 하는 정점들을 파악해나감
    • 주어진 키 비교값을 통해 정점간의 선후 관계를 indegree 배열로 표현
      만약 1 2 라면 1 -> 2 이므로, 2의 indegree 값에 1이 추가됨
    • 맨 처음부터 indegree의 값이 0인 정점들을 모두 큐에 추가
      (indegree가 0이라는 것은 곧 반드시 해당 정점보다 앞에 위치해야 하는 정점이 없다는 것과 같은 의미)
    • 큐가 빌 때까지 돌면서, 큐에서 빼낸 정점에서 뻗어나가는 정점들의 indegree 값을 1 감소시킴
      (큐에서 빼낸 정점을 의미적으로 삭제하기 위함)
    • 감소시킨 indegree값이 0이라면 앞선 로직처럼 큐에 추가

 

Swift를 활용한 풀이

let nm = readLine()!.split(separator: " ").map{Int($0)!}
var ind = Array(repeating: 0, count: nm[0]+1) // 1-index
var adj:[Int:[Int]] = [:]

for n in 1...nm[0] { adj[n] = [] }

for _ in 0..<nm[1] {
    let ab = readLine()!.split(separator: " ").map{Int($0)!}
    ind[ab[1]] += 1
    adj[ab[0]]!.append(ab[1])
}

var q:[Int] = []
for i in 1...nm[0] {
    if(ind[i] == 0) { q.append(i) } // 처음부터 indegree가 0인 정점을 큐에 추가해둠
}

var answer = ""
while(!q.isEmpty) {
    // 큐에 추가된 정점을 하나씩 꺼내어, 해당 정점에서 뻗어나가는 정점들의 indegree를 1 감소시킴
    // 감소된 indegree값이 0이라면, 해당 정점의 앞에 위치해야 하는 정점들이 모두 사라진 것으로 간주되므로 큐에 추가
    let cur = q.removeFirst()
    answer += "\(cur) "
    for n in adj[cur]! {
        ind[n] -= 1
        if(ind[n] == 0) { q.append(n) }
    }
}

print(answer)

 

 

JAVA를 활용한 풀이

import java.util.*;
import java.lang.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        StringTokenizer st = new StringTokenizer(br.readLine());

        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());

        int[] ind = new int[N+1];
        HashMap<Integer, ArrayList<Integer>> adj = new HashMap<>();

        for(int i=0; i<M; i++) {
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());

            ind[b]++;

            ArrayList<Integer> list = adj.getOrDefault(a, new ArrayList<Integer>());
            list.add(b);
            adj.put(a, list);
        }

        Queue<Integer> q = new LinkedList<>();
        for(int i=1; i<=N; i++) {
            if(ind[i] == 0) q.offer(i);
        }

        while(!q.isEmpty()) {
            int cur = q.poll();
            sb.append(cur).append(" ");

            if(adj.get(cur) != null) {
                for(int n : adj.get(cur)) {
                    ind[n]--;
                    if(ind[n] == 0) q.offer(n);
                }
            }
        }

        System.out.println(sb);
    }
}

문제 URL : https://solvesql.com/problems/daily-arppu/

 

https://solvesql.com/problems/daily-arppu/

 

solvesql.com

 

*문제 저작권으로 인하여 직접 작성한 쿼리문만 첨부

select date(order_purchase_timestamp) as dt,
count(distinct olist_orders_dataset.order_id) as pu,
sum(payment_value) as revenue_daily,
round(sum(payment_value)/count(distinct olist_orders_dataset.order_id), 2) as arppu
from olist_orders_dataset left join olist_order_payments_dataset
on olist_orders_dataset.order_id = olist_order_payments_dataset.order_id
where dt >= '2018-01-01'
group by dt
order by dt

 

중복되는 주문ID가 존재하여 DISTINCT 사용

또한 중복으로 인하여 AVG 함수를 사용하면, 나누어지는 값이 중복을 포함하여 더 크므로 평균값이 기댓값보다 낮게 나옴

이를 해결하고자 (총 금액)/(중복을 제외한 주문건수)로 평균값을 구함

문제 URL : https://solvesql.com/problems/estimated-delivery-date/

 

https://solvesql.com/problems/estimated-delivery-date/

 

solvesql.com

 

*문제 저작권으로 인하여 직접 작성한 쿼리문만 첨부

select date(order_purchase_timestamp) as purchase_date,
count(case when order_delivered_customer_date <= order_estimated_delivery_date then order_id end) as success,
count(case when order_delivered_customer_date > order_estimated_delivery_date then order_id end) as fail
from olist_orders_dataset
where purchase_date like '2017-01-%'
group by purchase_date
order by purchase_date

 

CASE WHEN절과 COUNT함수를 함께 사용하여, 특정 경우에 해당하는 행만 집계함

특정 년월에 해당하는 조건 설정을 위해 LIKE절과 와일드카드 사용

(SQLite 기반으로, date 데이터 내 년,월,일 정보를 추출하는 것은 year 등의 메소드가 아니라 strftime 메소드를 사용해야 함)

'Problem Solving' 카테고리의 다른 글

[백준] 2252-줄 세우기  (1) 2025.02.15
[SolveSQL] 쇼핑몰의 일일 매출액과 ARPPU  (0) 2025.02.06
[백준] 1715-카드 정렬하기  (0) 2025.02.05
[백준] 11286-절댓값 힙  (0) 2025.02.05
[백준] 1927-최소 힙  (0) 2025.02.05

 

코드 실행 시, "실행 시 검은 화면만 나오는 경우"  혹은 "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value"의 오류가 발생하는 경우 (로드 과정에서 컴포넌트 속성을 수정하는 경우 ex) label.text = "...")

 

이번 경우에는 화면 이동을 위해 SceneDelegate.swift 내에서, 아래와 같이 코드를 추가해서 문제가 발생함

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }

    let window = UIWindow(windowScene: windowScene)
    let rootVC = HomeViewController()   // 오류 발생 원인 코드
    let navigationController = UINavigationController(rootViewController: rootVC)

    window.rootViewController = navigationController
    self.window = window
    window.makeKeyAndVisible()
}

 

StoryBoard 기반의 UIKit를 사용하기 때문에, 단순히 저렇게 생성자를 호출하는 방식으로는 VC가 정상적으로 생성되지 않음

아래와 같이 identifier를 기반으로 instantiateViewController 메소드를 호출하여 초기화해야함

// 해결 코드
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootVC = storyboard.instantiateViewController(identifier: "HomeViewController") as! HomeViewController

 

 

+ Recent posts