[내일배움캠프][TIL] 23.12.08 (금) - 예상 대진표&최대값과 최소값, 키오스크 프로그램 Lv. 3
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
}
...음.
확실히 해설 영상보다는 덜 구조화 되어 있다.
역시 처음에 어떻게 구성하냐에 따라서 완전 다른 코드로 갈리게 되는 것 같다.