/*
There are numBottles water bottles that are initially full of water.
You can exchange numExchange empty water bottles from the market with one full water bottle.
The operation of drinking a full water bottle turns it into an empty bottle.
Given the two integers numBottles and numExchange,
return the maximum number of water bottles you can drink.
[Example 1]
https://assets.leetcode.com/uploads/2020/07/01/sample_1_1875.png
Input: numBottles = 9, numExchange = 3
Output: 13
Explanation: You can exchange 3 empty bottles to get 1 full water bottle.
Number of water bottles you can drink: 9 + 3 + 1 = 13.
[Example 2]
https://assets.leetcode.com/uploads/2020/07/01/sample_2_1875.png
Input: numBottles = 15, numExchange = 4
Output: 19
Explanation: You can exchange 4 empty bottles to get 1 full water bottle.
Number of water bottles you can drink: 15 + 3 + 1 = 19.
[Constraints]
1 <= numBottles <= 100
2 <= numExchange <= 100
[문제 링크]
https://leetcode.com/problems/water-bottles/
*/
export default function calculateNumberOfBottles(numBottles, numExchange) {
let interchangeableBottles = Math.floor(numBottles / numExchange);
let remainingBottles = numBottles % numExchange;
let bonusBottles = interchangeableBottles;
if (numBottles === numExchange) {
return numBottles + 1;
}
if (numBottles < numExchange) {
return numBottles;
}
while ((interchangeableBottles + remainingBottles) >= numExchange) {
bonusBottles += Math.floor((interchangeableBottles + remainingBottles) / numExchange);
interchangeableBottles = Math.floor((interchangeableBottles + remainingBottles) / numExchange);
remainingBottles = interchangeableBottles % numExchange;
}
return numBottles + bonusBottles;
}
export default function calculateNumberOfBottles(numBottles, numExchange) {
let result = numBottles;
let empty = numBottles;
while (Math.floor(empty / numExchange)) {
const exchanged = Math.floor(empty / numExchange);
result += exchanged;
empty = exchanged + (empty % numExchange);
}
return result;
}
// ✅ test code
import { expect } from "chai";
import calculateNumberOfBottles from "../lib/03-waterBottle";
describe("3. Calculate Number of water bottles", () => {
it("It should pass basic cases", () => {
expect(calculateNumberOfBottles(9, 3)).to.eql(13);
expect(calculateNumberOfBottles(15, 4)).to.eql(19);
expect(calculateNumberOfBottles(5, 5)).to.eql(6);
expect(calculateNumberOfBottles(2, 3)).to.eql(2);
expect(calculateNumberOfBottles(17, 3)).to.eql(25);
});
});
이번 알고리즘을 통해 느낀 점은 사소한 부분이긴 하지만,
1) 현재 연산에서는 복잡한 연산이 존재하지 않기 때문에 굳이 early return을 쓰지 않아도 된다는 점이다.
문제의 난이도가 있고 while문 내에서 공간/시간 복잡도에 차이가 커지는 이유가 있다면 당연히 early return을 써주겠지만, 현재 로직에서는 굳이 필요하지 않다는 점.
2) while을 언제 사용할까?
나는 보통 동일한 조건을 계속 반복해야할 때 자주 썼었다.
ex) 어느 현 시점이 되기 전까지 어떤 동작을 계속 반복해야할 경우
for문
과 while문
의 차이점은 무엇일까?🧐for 문
을 사용하는 것이 가독성이 더 좋고, 반복 횟수를 예측할 수 없으면 while 문
을 사용하는 것이 더 적합한 경우가 많은 것 같다.3) 또 하나는 나는 불필요하게 전역에 변수를 3개나 설정을 해주었는데, 다른 분의 풀이에서는 while문 내에서 하나의 변수를 선언해주고 전역에서는 2개의 변수만 선언해주어 불필요한 부분에서 변수를 선언하지 않고, 조금 더 간결하게 로직을 짰다는 점이다.
다른 분의 풀이를 보았다가 내 풀이를 다시 보았을 때, 가독성이 떨어지고 한 눈에 들어오지 않음을 느꼈다.
문제 출처) leetcode