[LeetCode]Unique paths

Icarus<Wing>ยท2025๋…„ 5์›” 22์ผ
post-thumbnail

https://leetcode.com/problems/unique-paths/description/

๐Ÿ“œ๋ฌธ์ œ ํ•ด์„

m x n grid์— ๋กœ๋ด‡์ด ์žˆ๋‹ค. ๋กœ๋ด‡์€ ์ฒ˜์Œ์— grid[0][0]์— ์œ„์น˜ํ•ด ์žˆ๋‹ค. ๋กœ๋ด‡์€ grid[m-1][n-1]๋กœ ์ด๋™ํ•˜๋ ค๊ณ  ํ•œ๋‹ค. ๋กœ๋ด‡์€ ํ•œ๋ฒˆ ์›€์ง์ผ ๋•Œ ์•„๋ž˜ ๋˜๋Š” ์˜ค๋ฅธ์ชฝ์œผ๋กœ ๋ฐ–์— ์ด๋™ํ•˜์ง€ ๋ชปํ•œ๋‹ค.

์ด๋•Œ intํ˜• m, n์ด ์ฃผ์–ด์กŒ์„ ๋•Œ, ๋กœ๋ด‡์ด grid[m-1][n-1]๋กœ ๋„์ฐฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€๋Šฅํ•œ unique paths์˜ ์ˆ˜๋ฅผ ๋ฆฌํ„ดํ•˜๋ผ.
(๋‹จ, ๋‹ต์€ 2 * 10^9 ์ดํ•˜์˜ ์‹œ๊ฐ„๋ณต์žก๋„๋ฅผ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค.)

๐Ÿšง์ œ์•ฝ ์กฐ๊ฑด
1 <= m, n <= 10^2
=> ๋งŒ์•ฝ ์™„์ „ํƒ์ƒ‰ํ•  ๊ฒฝ์šฐ, O(N^2) = 10^4์ด๋ฏ€๋กœ ๊ฐ€๋Šฅ!
But recursive function์œผ๋กœ ๊ตฌํ˜„ํ•  ๊ฒฝ์šฐ -> O(2^N) = 2^100์€ ๋ถˆ๊ฐ€!

โš™๏ธ์ฝ”๋“œ ์„ค๊ณ„

์ฒ˜์Œ์—๋Š” ๋ฐ”๋กœ mississippi์™€ ๊ฐ™์ด ์ˆœ์—ด์ด ๋– ์˜ฌ๋ผ์„œ ์ค‘๋ณต๋œ ์›์†Œ๋ฅผ ๋‚˜๋ˆ„๋Š” ๊ฒƒ์œผ๋กœ ์ ‘๊ทผํ–ˆ๋‹ค. ์ฆ‰, ์œ„์™€ ๊ฐ™์€ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๊ฐ€ ์žˆ์„ ๋•Œ, 8!/(6!โˆ—2!)8! / (6!*2!)๊ฐ€ ๋‚˜์˜จ๋‹ค.
์ด๊ฒƒ์„ ์ผ๋ฐ˜ํ™”์‹œํ‚ค๋ฉด, (m+nโˆ’2)!/((nโˆ’1)!โˆ—(mโˆ’1)!)(m+n-2)! / ((n-1)!*(m-1)!)๋กœ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ’ป์ฝ”๋“œ ๊ตฌํ˜„

public class UniquePathSolution {
    public int uniquePaths(int m, int n) {
        HashMap<Integer, Integer> memo = new HashMap<>();
        return fact(n + m - 2, memo) / (fact(n - 1, memo) * fact(m - 1, memo));
    }

    private static int fact(int x, HashMap<Integer, Integer> memo) {
        // base condition
        if (x <= 1) {
            return 1;
        }

        if (!memo.containsKey(x)) {
            memo.put(x, fact(x - 1, memo) * x);
        }
        return memo.get(x);
    }
}
  • ๐Ÿšฉfact() ํ•จ์ˆ˜๋ฅผ ์ค‘๋ณต ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์–ด์„œ DP๋ฅผ ๋ถ€๋ถ„์ ์œผ๋กœ ์ ์šฉํ•ด๋ดค๋‹ค. ๊ทธ๋Ÿฐ๋ฐ, ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ ๋•Œ๋ฌธ์— ์„ฑ๊ณต์ ์œผ๋กœ ์ œ์ถœ์ด ์•ˆ๋œ๋‹ค:
  1. m = n์ผ ๊ฒฝ์šฐ(n > 3), 2!โˆ—(nโˆ’1)!/((nโˆ’1)!โˆ—(nโˆ’1)!)2! * (n-1)! / ((n-1)! * (n-1)!)
    = 2/(nโˆ’1)!2 / (n-1)! ์ด ๋˜์„œ int ํƒ€์ž…์œผ๋กœ ๋ฆฌํ„ดํ•˜๋ฉด ๋‹น์—ฐํžˆ 0์ด ๋‚˜์˜ฌ ์ˆ˜ ๋ฐ–์— ์—†๋‹ค.
  2. ์ตœ๋นˆ๊ฐ’์ธ m = 100, n = 99์™€ ๊ฐ™์€ ๊ฐ’์„ input์œผ๋กœ ๋„ฃ์„ ๊ฒฝ์šฐ, signed int์˜ ์ตœ๋Œ€๊ฐ’์ธ 21์–ต์ด ๋„˜์–ด์„œ overflow๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ output์„ ์ดˆ๋ž˜ํ•œ๋‹ค.

๐Ÿค”๋”ฐ๋ผ์„œ ์กฐํ•ฉ๋ก ์œผ๋กœ ์ ‘๊ทผํ•˜๋Š” ๋Œ€์‹ , ์ง์ ‘ ๊ทธ๋ฆผ์„ ๊ทธ๋ ค๊ฐ€๋ฉด์„œ ์ ์ ˆํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ฐพ๋Š” ์ˆ˜ ๋ฐ–์— ์—†๋‹ค.

๐Ÿšฉ์ปค์Šคํ…€ ํด๋ž˜์Šค๋ฅผ map์— ๋„ฃ์„ ์ˆ˜ ์—†๋Š” ์ด์œ 

memo.put(new Point(0, 0))๊ณผ ๊ฐ™์ด ์ปค์Šคํ…€ ํด๋ž˜์Šค๋ฅผ HashMap์˜ ํ‚ค(key)๋กœ ์‚ฌ์šฉํ•  ๋•Œ, dp์—์„œ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•˜๋‹ค๊ฐ€ ๋™์ผํ•œ ์˜๋ฏธ์˜ ์ขŒํ‘œ๊ฐ€ ์ „๋‹ฌ๋˜๋”๋ผ๋„ new Point(0, 0)์ฒ˜๋Ÿผ ์ƒˆ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๋ฉด ์„œ๋กœ ๋‹ค๋ฅธ ๊ฐ์ฒด๋กœ ๊ฐ„์ฃผ๋œ๋‹ค.
์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ฒˆ๊ฑฐ๋กญ๊ฒŒ hashCode()์™€ equals()๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ํ•ด์•ผ ํ•˜๋Š”๋ฐ ์ฝ”๋“œ๊ฐ€ ๋งค์šฐ ์ง€์ €๋ถ„ํ•˜๋ฏ€๋กœ,
๐Ÿ”จ๋ฐฐ์—ด ํƒ€์ž…์˜ dp๋ฅผ ์‚ฌ์šฉํ•˜์ž.

โš™๏ธ์ˆ˜์ •๋œ ์ฝ”๋“œ ์„ค๊ณ„

์ •๋ง ๊ฐ„๋‹จํ•˜๊ฒŒ ๋…ธํŠธ์— grid ๊ทธ๋ฆผ ๊ทธ๋ฆฌ๊ณ  ์ค‘๊ณ ๋”ฉ ์‹œ์ ˆ๋•Œ ํ–ˆ๋˜ ๋ฐฉ๋ฒ•์œผ๋กœ unique paths๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌํ•ด๋ณด์•˜๋‹ค.

1. ๊ฐ ๋ชจ์„œ๋ฆฌ๋กœ ๊ฐ€๋Š” ๋ฐฉ๋ฒ•์€ 1๊ฐ€์ง€ ๋ฐ–์— ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ์„œ๋ฆฌ๋ฅผ ๋ชจ๋‘ 1๋กœ ์ฑ„์šด๋‹ค.
2. ๊ฐ ์…€๋“ค์˜ ๋ฐฉ๋ฒ• ์ˆ˜๋ฅผ ๋”ํ•œ ๋‹ค์Œ, ์ข…์ ์— ๋„๋‹ฌํ•  ๋•Œ๊นŒ์ง€ ๋”ํ•˜๋Š” ๊ณผ์ •์„ ๋ฐ˜๋ณตํ•œ๋‹ค.

...์ฑ„์šด๋‹ค, ์ฑ„์šด๋‹ค, ๋ฐ”๋กœ tabulation ๊ธฐ๋ฒ•์ด ๋– ์˜ค๋ฅด์ง€ ์•Š์€๊ฐ€!?? ๊ทธ๋ ‡๋‹ค. ์ด ๋ฌธ์ œ๋Š” ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ Bottom-up์œผ๋กœ ํ’€ ์ˆ˜๊ฐ€ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

๐Ÿค”์•„๋‹ˆ grid๋Š” ์•”์‹œ์  ๊ทธ๋ž˜ํ”„๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ๊นŒ bfs๋‚˜ dfs๋กœ๋„ ํ’€ ์ˆ˜ ์žˆ๋Š”๊ฑฐ ์•„๋‹Œ๊ฐ€์š”?
๐Ÿ‘‰ใ„ดใ„ด. ์ด์ „์— ํ’€์—ˆ๋˜ The shortest path in a binary matrix์—์„œ๋Š” ์‹œ์ ์—์„œ (dr, dc ์„ธํŒ…ํ•ด์„œ)bfs๋กœ ๊ฐ€์žฅ ์งง์€ ๊ฒฝ๋กœ๋ฅผ ๊ตฌํ–ˆ์ง€๋งŒ, ์ด ๋ฌธ์ œ๋Š” ๊ฒฝ๋กœ(๋ฐฉ๋ฒ•)์˜ ๊ฐœ์ˆ˜๋ฅผ ๊ตฌํ•˜๋Š” ๊ฒƒ์ด ๋ชฉ์ ์ด๋ฏ€๋กœ dp๊ฐ€ ๊ฐ€์žฅ ์ ์ ˆํ•˜๋‹ค.

๐Ÿฏ๋ฌธ์ œ์—์„œ dp๋กœ ํ’€๋ผ๋Š” ํžŒํŠธ๋“ค ์ฐธ๊ณ  ํ•  ๊ฒƒ!

๋”ฐ๋ผ์„œ tabulation ๊ธฐ๋ฒ•์„ ํ™œ์šฉํ•ด์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ’ป์ฝ”๋“œ ๊ตฌํ˜„

public class UniquePathSolution {
    public int uniquePaths(int m, int n) {
        // m x n ํฌ๊ธฐ์˜ dp๋ฅผ ์ดˆ๊ธฐํ™”
        int[][] dp = new int[m][n];

        // ๋ชจ์„œ๋ฆฌ๋ฅผ 1๋กœ ๋ชจ๋‘ ์ฑ„์šด๋‹ค.
        for (int i = 0; i < m; i++) {
            dp[i][0] = 1;
        }
        for (int j = 0; j < n; j++) {
            dp[0][j] = 1;
        }

        // ๋ชจ์„œ๋ฆฌ์˜ ํ•ฉ์œผ๋กœ ์ข…์ ์˜ ๊ฐ’์„ ๋ฆฌํ„ด
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
        return dp[m - 1][n - 1];
    }
}
  • m = 1, n = 1์ผ ๋•Œ์™€ ๊ฐ™์ด ์‹œ์ ๊ณผ ์ข…์ ์ด ๊ฐ™์„ ๋•Œ์˜ output์ด 1์ด๋ฏ€๋กœ 1๋กœ ์ฑ„์šฐ๋Š” ๋กœ์ง์—์„œ i, j์˜ ์‹œ์ž‘์ ์„ 0์œผ๋กœ ์„ค์ •ํ•˜์˜€๋‹ค.
  • ๊ฐ ๋ชจ์„œ๋ฆฌ์˜ ํ•ฉ์œผ๋กœ ๊ฒฝ๋กœ๋ฅผ ๊ฐฑ์‹ ํ•˜๊ธฐ ์œ„ํ•ด dp[i][j] = dp[i - 1][j] + dp[i][j - 1]๋กœ ๊ณ„์†ํ•ด์„œ grid์˜ ๊ฐ’์„ ์ฑ„์›Œ๋‚˜๊ฐ”๋‹ค.

๐Ÿ˜ฎ๊ทธ๋Ÿฐ๋ฐ, LeetCode์˜ ํ•œ ์ฒœ์žฌ์ ์ธ ์‚ฌ์šฉ์ž๊ฐ€ ์•„๋ž˜ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด grid๋ฅผ ์‹œ๊ณ„ ๋ฐฉํ–ฅ์œผ๋กœ 45๋„ ๋Œ๋ฆฌ๋ฉด ์ •ํ™•ํžˆ ํŒŒ์Šค์นผ์˜ ์‚ผ๊ฐํ˜•์ด ๋‚˜์˜จ๋‹ค๊ณ  ํ•˜์˜€๋‹ค!!

๊ทธ๋ ‡๋‹ค. ๊ฒฝ๋กœ๋ฅผ ๊ฐฑ์‹ ํ•˜๊ธฐ ์œ„ํ•œ ์ € ์ˆ˜์‹์€ ์‚ฌ์‹ค์€ ํŒŒ์Šค์นผ์˜ ์‚ผ๊ฐํ˜• ๋ฒ•์น™์ธ (nr)=(nโˆ’1rโˆ’1)+(nโˆ’1r)\binom{n}{r} = \binom{n-1}{r-1} + \binom{n-1}{r}์—์„œ ๋‚˜์˜จ ๊ฒƒ์ด์˜€๋‹ค.

โฐ์ฝ”๋“œ์˜ ์‹œ๊ฐ„๋ณต์žก๋„

๋ชจ๋“  grid๋ฅผ ์ˆœํšŒํ•ด์„œ ๊ฐ’์œผ๋กœ ์ฑ„์šฐ๊ธฐ ๋•Œ๋ฌธ์— O(Mโˆ—N)O(M*N)์ด ๊ฑธ๋ฆฐ๋‹ค.

โš™๏ธTop-down ์ฝ”๋“œ ์„ค๊ณ„

  1. memo ํ…Œ์ด๋ธ”์— -1์„ ๋ชจ๋‘ ์ฑ„์šด๋‹ค.
    Let m - 1 = r, n - 1 = c
  2. dp ํ˜ธ์ถœ ์กฐ๊ฑด(๋Œ€์ „์ œ): if memo[r][c] == -1:
  3. base condtiion:
if r == 0 and c == 0: 
	memo[r][c] = 1
    return memo[r][c]
  1. ์™ผ์ชฝ ๋˜๋Š” ์œ„์—์„œ๋ถ€ํ„ฐ ์˜ค๋Š” ๊ฒฝ๋กœ๋ฅผ ํ•œ ์ชฝ๋งŒ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด dp๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค int uniquePath = 0์œผ๋กœ ์„ค์ •
  2. ์ ํ™”์‹์„ ์„ธ์›Œ์„œ dp๋ฅผ ํ˜ธ์ถœ
  3. ๊ฐ’์ด ์žˆ์œผ๋ฉด memo[r][c]๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฆฌํ„ดํ•ด์„œ callerํ•œํ…Œ ์ „๋‹ฌ

๐Ÿ’ปTop-down ์ฝ”๋“œ ๊ตฌํ˜„

public class UniquePathSolution {
    public int uniquePaths(int m, int n) {
        int[][] memo = new int[m][n];

        // ํ–‰์„ -1๋กœ ์ฑ„์šด๋‹ค
        for (int i = 0; i < m; i++) {
            Arrays.fill(memo[i], -1);
        }

        return dp(m - 1, n - 1, memo);
    }

    private static int dp(int r, int c, int[][] memo) {
        // base condition
        if (r == 0 && c == 0) {
            memo[r][c] = 1;
            return memo[r][c];
        }

        int uniquePath = 0; 

        // dp ํ˜ธ์ถœ ์กฐ๊ฑด
        if (memo[r][c] == -1) {
            if (r > 0) {
                uniquePath += dp(r - 1, c, memo);
            }
            if (c > 0) {
                uniquePath += dp(r, c - 1, memo);
            }
            memo[r][c] = uniquePath; // ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ์ด์šฉ
        }
        return memo[r][c];
    }

}
  • caller๊ฐ€ ํ˜ธ์ถœํ•  ๋•Œ int uniquePath = 0๋กœ ์„ค์ •ํ•œ ์ด์œ ๋Š”, ์œ„์—์„œ ์˜ค๋Š” ๊ฐ’๊ณผ ์™ผ์ชฝ์—์„œ ์˜ค๋Š” ๊ฐ’์„ ์ดˆ๊ธฐ ์ƒํƒœ๋กœ๋ถ€ํ„ฐ ๊ฐ๊ฐ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋‹ค.
    ์ฆ‰, caller ํ•จ์ˆ˜์˜ uniquePath์™€ callee ํ•จ์ˆ˜์˜ uniquePath๋Š” ๊ฐ๊ฐ ๋‹ค๋ฅธ ์Šคํƒ ํ”„๋ ˆ์ž„์— ์กด์žฌํ•˜๋ฏ€๋กœ, ์„œ๋กœ ๋…๋ฆฝ์ ์ธ ๊ฐ’์„ ๊ฐ€์ง€๊ฒŒ ๋˜๊ธฐ ๋•Œ๋ฌธ์— callee์—์„œ ๊ณ„์‚ฐํ•œ ๊ฐ’์„ ๋ฆฌํ„ดํ•ด์„œ caller ํ•จ์ˆ˜๋กœ ๋ˆ„์ ํ•ด์„œ ๊ฐ’์„ ๊ตฌํ•  ์ˆ˜๊ฐ€ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.
    • ๐Ÿ”Žํ—ฌํผ ๋ฉ”์„œ๋“œ์ธ dp์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌํ•œ memo๋Š” Reference type์ด๋ฏ€๋กœ, ๊ฐ์ฒด์˜ ์ฃผ์†Œ๊ฐ’์„ ์—ฌ์ „ํžˆ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์Šคํƒ ํ”„๋ ˆ์ž„๊ณผ ์ƒ๊ด€์—†์ด ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋œ๋‹ค.

๐Ÿ“๊นจ๋‹ฌ์€ ์ 

๐Ÿ“œ๋ฌธ์ œ ์กฐ๊ฑด์—์„œ "๋กœ๋ด‡์€ ํ•œ๋ฒˆ ์›€์ง์ผ ๋•Œ ์•„๋ž˜ ๋˜๋Š” ์˜ค๋ฅธ์ชฝ์œผ๋กœ ๋ฐ–์— ์ด๋™ํ•˜์ง€ ๋ชปํ•œ๋‹ค."๋ผ๋Š” ๊ฒƒ์€ Bottom-up์—์„œ๋Š” ์ ํ™”์‹์—์„œ ๋”ํ•˜๊ธฐ ์—ฐ์‚ฐ์ž๋กœ, Top-down์—์„œ๋Š” if๋ฌธ ๋ฐ dp ํ˜ธ์ถœ์„ ๋”ฐ๋กœ๋”ฐ๋กœ ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

grid๊ฐ€ ๋ณด์ธ๋‹ค๊ณ  ๋ฌด์ง€์„ฑ์œผ๋กœ bfs ๋˜๋Š” dfs๋ฅผ ์“ธ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ๋ฐ˜๋“œ์‹œ ์ž๋ฃŒ๊ตฌ์กฐ ๋ฐ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์„ ํƒํ•˜๊ธฐ ์ „์— naiveํ•˜๊ฒŒ ๋ฌธ์ œ๋ฅผ ๋จผ์ € ํ’€์–ด๋ณด๊ณ  ์„ ํƒํ•  ๊ฒƒ!

profile
๋ชจ๋“  ์ฝ”๋“œ์—๋Š” ์ด์œ ๊ฐ€ ์žˆ๊ธฐ์— ์›์ธ์„ ํŒŒ์•…ํ•  ๋•Œ๊นŒ์ง€ ์ง‘์š”ํ•˜๊ฒŒ ํƒ๊ตฌํ•˜๋Š” ๊ฒƒ์„ ์ข‹์•„ํ•ฉ๋‹ˆ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€