[TIL][내일배움캠프]

[내일배움캠프][TIL] 23.12.08 (금) - 예상 대진표&최대값과 최소값, 키오스크 프로그램 Lv. 3

kimlaurant 2023. 12. 8. 20:37
1. 코드카타

 

오늘 푼 알고리즘 문제는 '예상 대진표' 이다.

 

 예상 대진표
대회는 N명이 참가하고, 토너먼트 형식으로 진행됩니다. N명의 참가자는 각각 1부터 N번을 차례대로 배정받습니다. 그리고, 1번↔2번, 3번↔4번, ... , N-1번↔N번의 참가자끼리 게임을 진행합니다. 각 게임에서 이긴 사람은 다음 라운드에 진출할 수 있습니다. 이때, 다음 라운드에 진출할 참가자의 번호는 다시 1번부터 N/2번을 차례대로 배정받습니다. 만약 1번↔2번 끼리 겨루는 게임에서 2번이 승리했다면 다음 라운드에서 1번을 부여받고, 3번↔4번에서 겨루는 게임에서 3번이 승리했다면 다음 라운드에서 2번을 부여받게 됩니다.
게임 참가자 수 N, 참가자 번호 A, 경쟁자 번호 B가 함수 solution의 매개변수로 주어질 때, 처음 라운드에서 A번을 가진 참가자는 경쟁자로 생각하는 B번 참가자와 몇 번째 라운드에서 만나는지 return 하는 solution 함수를 완성해 주세요. 
단, A번 참가자와 B번 참가자는 서로 붙게 되기 전까지 항상 이긴다고 가정합니다.

 

오늘도 문자열보다는 수학적인 개념이 들어간 문제다.

 

이번 문제의 답은 뭐 복잡한 수학적인 식을 요구하는 것도 아니고, 그렇다고 내가 모르는 생소한 지식을 요구하는 것도 아니기 때문에 곧바로 코드부터 공개하겠다.

 

class Solution {
    fun solution(n: Int, a: Int, b: Int): Int {
        var answer = 0
        var A = a
        var B = b
 
        while (A != B) {
            answer++
            A = (A+1)/2
            B = (B+1)/2
        }

        return answer
    }
}

 

여기서 중요한 건 왜 저런 식이 나왔냐는 건데 하나하나 뜯어보면

  • A != B : 이건 본인들끼리 대결하는 것 또한 포함시켜야 하기 때문에 넣었다. 참고로 본인들끼리 붙으면 이제 둘의 숫자가 같아지므로 while문을 벗어나게 된다.
  • A = (A+1) / 2 -> 새로운 번호를 부여할 때, 16강, 8강, 4강 이런 식으로 2씩 나누어지기 때문에 번호 역시 똑같이 2를 나누어준다. 이 때, A+1을 넣은 이유는 홀수인 경우 1이 감소하기 때문이다. (짝수는 1을 넣어도 어차피 홀수가 되어서 값에 변화가 없음.)

어때요, 참 쉽죠?

 

 

하지만, 너무 빨리 끝나서 이대로 끝내기에는 뭔가 아쉬웠다.

 

그래서 하나 더 풀어보기로 했다.

 

 최대값과 최소값
문자열 s에는 공백으로 구분된 숫자들이 저장되어 있다. str에 나타나는 숫자 중 최소값과 최대값을 찾아 이를 "(최소값) (최대값" 형태의 문자열을 반환하는 함수, solution을 완성하라.

 

문제 자체는 간단한데, 답을 도출하는 과정이 묘하게 까다롭다.

그 이유는 바로 문자열이기 때문.

 

이게 최대값과 최소값을 구분하려면 정수 배열을 통해서 식을 넣어야 하는데 이거는 문자열이기 때문에 이 자체만으로는 배열로 사용할 수가 없다.

 

그렇기 때문에 이 문제에서는 정수 배열로 만드는 과정이 필요하다.

그리고 그 방법은 지난 시간에 이미 배운 것이 하나 있다.

val numbers = s.split(" ").map{it.toInt()}

 

바로 이거.

이걸 바로 써먹을 날이 올 줄은 몰랐네.

 

이제 여기서 배열로 만든 다음에 최대값과 최소값을 찾고, 그걸 다시 문자열로 반환시키면

class Solution {
    fun solution(s: String): String {
        val numbers = s.split(" ").map{it.toInt()}
        
        var min = numbers.minOrNull()
        var max = numbers.maxOrNull()
        
        return "${min} ${max}"
    }
}

 

이 식이 나오게 된다.

 

이번 문제는 문자열을 어떻게 정수형 배열로 바꿀 수 있느냐가 관건이었다.

 

 

 

2. 키오스크 프로그램 Lv. 3

 

어제는 기본적인 인터페이스만 만든 Lv. 1과 클래스를 엮어서 만든 Lv. 2을 만들었다.

오늘은 여기에 이어서 각 클래스끼리 상속을 하고, 객체들을 리스트화 시키는 Lv. 3에 들어가보도록 하겠다.

 

그런데 이번 과제는 부분적이지만, 팀 과제다.

팀 당 한 명 씩 어느 부분을 분담해서 코드를 짜는 정도까지는 아니고, 코드는 개인이서 짜되 거기서 가장 최적의 코드를 취합해서 제출하는 방식이다.

그래서 우선은 조원끼리 하나씩 만들어본 다음에 그 중에서 가장 괜찮은 코드를 선별해서 대표로 제출할 예정이다.

 

우선은 이번 과제의 해설 영상에서 나온 코드부터 참고해봤다.

 

package com.example.kiosk

var menus: MutableList<Menu> = ArrayList()
var foods: MutableList<Food> = ArrayList()

fun main() {
    init()

    while (true) {
        displayMenu()
        var selectNumber = getNumber()

        if (selectNumber == 0) break

        var selectedFood = selectMenu(selectNumber)
        }

    println ("프로그램을 종료합니다.")
}

fun init () {
    menus.add(Menu("프리미엄 와퍼"))
    menus.add(Menu("와퍼"))
    menus.add(Menu("사이드 메뉴"))
    menus.add(Menu("음료"))

    //프리미엄 와퍼 추가
    foods.add(Food("콰트로치즈와퍼", 7900, "프리미엄 와퍼"))
    foods.add(Food("통새우와퍼", 7900, "프리미엄 와퍼"))
    foods.add(Food("몬스터와퍼", 9300, "프리미엄 와퍼"))
    foods.add(Food("스태커와퍼", 13300, "프리미엄 와퍼"))

    //와퍼 추가
    foods.add(Food("와퍼", 7100, "와퍼"))
    foods.add(Food("치즈와퍼", 7700, "와퍼"))
    foods.add(Food("불고기와퍼", 7400, "와퍼"))
    foods.add(Food("와퍼주니어", 4700, "와퍼"))

    //사이드메뉴 추가
    foods.add(Food("감자튀김", 2100, "사이드 메뉴"))
    foods.add(Food("코울슬로", 2100, "사이드 메뉴"))
    foods.add(Food("너겟킹", 2200, "사이드 메뉴"))
    foods.add(Food("바삭킹", 2100, "사이드 메뉴"))

    //음료 추가
    foods.add(Food("콜라", 2000, "음료"))
    foods.add(Food("스프라이트", 2000, "음료"))
    foods.add(Food("오렌지쥬스", 2800, "음료"))
    foods.add(Food("아메리카노", 1500, "음료"))
}

fun displayMenu() {
    println("어서오세요 고객님.")
    println("아래의 메뉴 중에서 원하는 것을 고르세요.")

    var menuSize = menus.size
    var count = 1
    for (idx in 1..menuSize) {
        val menu = menus[idx-1]
        val name = menu.name
        println ("$idx. $name")
        count++
    }
    println ("0. 종료")
}

fun displayMenuDetail (categoryName: String) {
    println("\n[ $categoryName ]")

    var filteredFoods = foods.filter { it.category == categoryName }

    val maxNameLength = filteredFoods.maxOfOrNull { it.name.toString().length } ?: 0
    var foodSize = filteredFoods.size
    for(i in 1..foodSize) {
        val food = filteredFoods[i-1]
        val name = food.name
        val price = food.price
        val namePadding = " ".repeat(maxNameLength - name.length)
        println("$i. $name$namePadding | W $price")
    }
    println("0. 뒤로 가기")
}

fun getNumber(): Int {
    var userInput: String?
    var number: Int?

    while(true){
        print("번호를 입력해주세요.")
        userInput = readLine()
        number = userInput?.toIntOrNull()

        if(number != null) {
            return number
        } else {
            println("올바른 숫자를 입력해주세요")
        }
    }
}

fun selectMenu(cateNumber: Int) : Food? {
    var menu = menus[cateNumber - 1]
    var categoryName = menu.name

    if (categoryName != "Cancel") {
        var filteredFoods = foods.filter { it.category == categoryName }
        displayMenuDetail(categoryName)

        while (true) {
            var selectFoodNumber = getNumber()
            if (selectFoodNumber > filteredFoods.size || selectFoodNumber < 0) {
                println("올바른 숫자를 입력해주세요")
            } else if (selectFoodNumber == 0) {
                return null
            } else {
                return filteredFoods[selectFoodNumber - 1]
            }
        }
    }
    return TODO("Provide the return value") // 왜 때문인지는 모르겠는데 이거 안 넣으니 실행이 안 됨
}

 

open class Menu (name: String) {
    var idx: Int
    var name: String

    init {
        this.idx = getNextIdx()
        this.name = name
    }

    open fun displayInfo () {
        println ("ID: $idx, 이름: $name")
    }

    companion object {
        private var maxIdx = 1

        private fun getNextIdx(): Int {
            return maxIdx++
        }
    }
}
class Food (name: String, price: Int, category: String) : Menu(name) {
    var price: Int
    var category: String

    init {
        this.price = price
        this.category = category
    }

    override fun displayInfo() {
        println ("ID: $idx, 이름: $name, 가격: $price, 카테고리: $category ")
    }
}

 

당연히 해설은 버거킹을 그대로 쓰진 않았고 다른 버거 브랜드를 썼다. (내가 약간 바꿨다.)

 

자세히 보니 해설영상 답게 군더더기 없이 깔끔하게 각 클래스마다 어떤 파라미터와 매개변수를 써야할지, 또 어떤 클래스를 만들고, 이걸 어떻게 객체리스트로 만들었는지 잘 나타내었다.

 

이건 이런 식으로 구성할 수도 있다는 것만 배우고 넘어갔다. 어쨌든 내가 짠 코드가 아니니까...

 

 

그 다음은 이걸 바탕으로 만든 코드들 중에서 가장 잘 나온 코드.

 

fun main() {
    println ("어서오세요 고객님.")
    println ("아래의 메뉴 중에서 원하는 것을 고르세요.")
    println ("")

    val menu = List()
    val money=Money()
    var selectNumber: Int

    while (true) {
        menu.menuList()
        selectNumber = readLine()?.toIntOrNull() ?: -1

        when (selectNumber) {
            1 -> primiumWapper(menu, money)
            2 -> wapper(menu, money)
            3 -> sideMenu(menu, money)
            4 -> drink(menu, money)
            9 -> pay(money)
            0 -> break
            else -> println("유효하지 않은 번호입니다.")
        }
    }

    println ("프로그램을 종료합니다.")
}


fun primiumWapper(menu: List, money: Money) {
    val myMenu :ArrayList<PrimiumWapper> = arrayListOf(QuacheeWapper(), ShrimpWapper(), MonsterWapper(), StackerWapper())
    var selectMenu: Int
    menu.primiumList()

    while (true) {
        selectMenu = readLine()?.toIntOrNull() ?: -1

        when (selectMenu) {
            in 1..4 -> {
                myMenu[selectMenu - 1].displayInfo()
                money.total += myMenu[selectMenu-1].price
            }
            0 -> break
            else -> println("유효하지 않은 번호입니다.")
        }
    }
}

fun wapper(menu: List, money: Money) {
    val myMenu :ArrayList<Wapper> = arrayListOf(WapperBurger(), CheeseWapper(), BulgogiWapper(), WapperJunior())
    var selectMenu: Int

    while (true) {
        menu.primiumList()
        selectMenu = readLine()?.toIntOrNull() ?: -1

        when (selectMenu) {
            in 1..4 -> {
                myMenu[selectMenu - 1].displayInfo()
                money.total += myMenu[selectMenu-1].price
            }
            0 -> break
            else -> println("유효하지 않은 번호입니다.")
        }
    }
}

fun sideMenu(menu: List, money: Money) {
    val myMenu :ArrayList<SideMenu> = arrayListOf(FrenchFried(), NuggetKing(), Coleslaw(), BasackKing())
    var selectMenu: Int

    while (true) {
        menu.primiumList()
        selectMenu = readLine()?.toIntOrNull() ?: -1

        when (selectMenu) {
            in 1..4 -> {
                myMenu[selectMenu - 1].displayInfo()
                money.total += myMenu[selectMenu-1].price
            }
            0 -> break
            else -> println("유효하지 않은 번호입니다.")
        }
    }
}

fun drink(menu: List, money: Money) {
    val myMenu :ArrayList<Drink> = arrayListOf(Coke(), Sprite(), Juice(), Coffee())
    var selectMenu: Int

    while (true) {
        menu.primiumList()
        selectMenu = readLine()?.toIntOrNull() ?: -1

        when (selectMenu) {
            in 1..4 -> {
                myMenu[selectMenu - 1].displayInfo()
                money.total += myMenu[selectMenu-1].price
            }
            0 -> break
            else -> println("유효하지 않은 번호입니다.")
        }
    }
}

fun pay(money: Money) {
    println ("결제수단을 선택하세요.")
    println ("1. 현금")
    println ("2. 카드")
    println ("3. 주문 취소")
    println ("0. 뒤로 가기")

    val selectMenu = readLine()!!.toInt()

    while (true){
        when (selectMenu) {
            in 1..2 -> {
                if (money.amount >= money.total) {
                    money.amount -= money.total
                    println("결제가 되었습니다.")
                } else {
                    println("잔액이 부족합니다.")
                }
                break
            }

            3 -> {
                println("주문을 취소합니다.")
                money.total = 0
                break
            }

            0 -> break
            else -> println("유효하지 않은 번호입니다.")
        }
    }
}

 

open class PrimiumWapper() {
    open var name: String = ""
    open var price: Int = 0
    fun displayInfo() {
        println ("${name} 주문하였습니다. 이 제품의 가격은 ${price} 원 입니다.")
    }
}

class QuacheeWapper () : PrimiumWapper() {
    override var name="콰트로치즈와퍼"
    override var price=7900
}

class ShrimpWapper () : PrimiumWapper() {
    override var name="통새우와퍼"
    override var price=7900
}

class MonsterWapper () : PrimiumWapper() {
    override var name="몬스터와퍼"
    override var price=9300
}

class StackerWapper () : PrimiumWapper() {
    override var name="스태커4 와퍼"
    override var price=13000
}
open class Wapper {
    open var name: String = ""
    open var price: Int = 0
    fun displayInfo() {
        println("${name} 주문하였습니다. 이 제품의 가격은 ${price} 원 입니다.")
    }
}

class WapperBurger : Wapper() {
    override var name = "와퍼"
    override var price = 7100
}

class CheeseWapper : Wapper() {
    override var name = "치즈와퍼"
    override var price = 7700
}

class BulgogiWapper : Wapper() {
    override var name = "불고기와퍼"
    override var price = 7400
}

class WapperJunior : Wapper() {
    override var name = "와퍼주니어"
    override var price = 4700
}
open class SideMenu() {
    open var name: String = ""
    open var price: Int = 0
    fun displayInfo() {
        println ("${name} 주문하였습니다. 이 제품의 가격은 ${price} 원 입니다.")
    }
}

class FrenchFried () : SideMenu() {
    override var name="감자튀김"
    override var price=2100
}
class Coleslaw () : SideMenu() {
    override var name="코올슬로"
    override var price=2100
}

class NuggetKing () : SideMenu() {
    override var name="너겟킹"
    override var price=2200
}

class BasackKing () : SideMenu() {
    override var name="바삭킹"
    override var price=3000
}
open class Drink() {
    open var name: String = ""
    open var price: Int = 0
    fun displayInfo() {
        println ("${name} 주문하였습니다. 이 제품의 가격은 ${price} 원 입니다.")
    }
}

class Coke () : Drink() {
    override var name="콜라"
    override var price=2000
}

class Sprite () : Drink() {
    override var name="스프라이트"
    override var price=2000
}

class Juice () : Drink() {
    override var name="오렌지쥬스"
    override var price=2800
}

class Coffee () : Drink() {
    override var name="아메리카노"
    override var price=1500
}

...음.

확실히 해설 영상보다는 덜 구조화 되어 있다.

 

역시 처음에 어떻게 구성하냐에 따라서 완전 다른 코드로 갈리게 되는 것 같다.