thảo luận Leetcode mỗi ngày

  • Người tạo chủ đề Người tạo chủ đề _Gia_Cat_Luong_
  • Ngày bắt đầu Ngày bắt đầu
Trạng thái
Không mở để trả lời thêm.
Java:
class Solution {
    fun search(nums: IntArray, target: Int): Boolean {
        val n = nums.size
        var lo = 0
        var hi = n - 1
        while (lo <= hi) {
            val mid = lo + (hi - lo) / 2
            if (nums[mid] == target) return true

            // lo <= mid
            // -- target < lo     -> lo = mid + 1
            // -- target > mid    -> lo = mid + 1
            // lo <= target < mid -> hi = mid - 1
            // lo > mid
            // -- target < mid    -> hi = mid - 1
            // -- target >= lo    -> hi = mid - 1
            // mid < target < lo  -> lo = mid + 1
            if (nums[lo] == nums[mid]) {
                lo++
                continue
            }
            if (nums[lo] <= nums[mid] && (target < nums[lo] || target > nums[mid]) || target < nums[lo] && target > nums[mid]) {
                lo = mid + 1
            } else {
                hi = mid - 1
            }
        }
        return false
    }
}
 
Mấy nay giải dynamic programming mới nhận ra chân lí là tìm cách giải brute force trước rồi tìm cách convert qua topdown. Xưa cứ lao đầu vào tìm cách giải topdown + bottom up trước ngáo vl :cautious:
 
Mấy nay giải dynamic programming mới nhận ra chân lí là tìm cách giải brute force trước rồi tìm cách convert qua topdown. Xưa cứ lao đầu vào tìm cách giải topdown + bottom up trước ngáo vl :cautious:

Tôi thì cứ suy nghĩ đệ quy trước rồi kèm memo table vào cho nhẹ đầu :sweet_kiss:
 
Tôi thì cứ suy nghĩ đệ quy trước rồi kèm memo table vào cho nhẹ đầu :sweet_kiss:
Như bài hôm nay chả hạn my fence.
Đầu tiên mình vẽ decision tree rồi code brute force
1691717731323.png


C#:
public class Solution {
    private int result = 0;
    private int[] coins;
    public int Change(int amount, int[] coins) {
         this.coins = coins;
         NumFlips(0, amount);
         return result;
    }

    private void NumFlips(int index, int amount)
    {
        if(amount == 0) {
            this.result ++;
            return;
        }

        if(amount < 0 || index >= coins.Length)
            return;

        NumFlips(index, amount - coins[index]);
        NumFlips(index + 1, amount);
    }
}
Chạy kiểm chứng kết quả, ra ngay TLE.
Sửa lại 1 tí từ Brute force qua topdown
C#:
public class Solution {
    private int[] coins;
    public int Change(int amount, int[] coins) {
         this.coins = coins;
         var dp = new int[coins.Length + 1, amount + 1];
         for(int i = 0; i<= coins.Length; i++)
         {
             for(int j = 0; j <= amount; j++)
             {
                 dp[i,j] = -1;
             }
         }
         return NumFlips(0, amount, dp);
    }

    private int NumFlips(int index, int amount, int[,] dp)
    {
        if(amount == 0) {
            return 1;
        }

        if(amount < 0 || index >= coins.Length)
            return 0;

        if(dp[index, amount] == -1)
        {
            dp[index, amount] = NumFlips(index, amount - coins[index], dp) + NumFlips(index + 1, amount, dp);
        }

        return dp[index,amount];
    }
}
Chiến lược quá ok cho newbie như mình :sure: làm thành 2D DP như thật. Chắc cũng sẽ tìm ra được bottom up từ topdown nhưng mà cứ từng bước từ từ đã. Chính thức cán mốc 200 bài code thiếu nhi :doubt: thời gian tổng cộng code bài này chưa tới 15ph. Ko tìm ra topdown thì chắc cũng ko fail PV được :ah:
 
Straightforward: Với mỗi đồng xu, ta sẽ chạy vòng lặp từ giá trị đồng xu đó coin đến giá trị cần tìm amount. Mỗi 1 giá trị i thì sẽ cộng thêm 1 khoảng dp[i-coin]. Để đạt được amount -> dp[amount]


JavaScript:
function change(amount: number, coins: number[]): number {
    const dp: number[] = new Array(amount + 1).fill(0);
    dp[0] = 1;
    for (const coin of coins) {
        for (let i = coin; i <= amount; i++) {
            dp[i] += dp[i - coin];
        }
    }
    return dp[amount];
}
Thực ra là cũng éo straightforward, cũng là nghĩ từ DP Bottom-up đi ra :shame:
 
Sửa lần cuối:
Mấy nay giải dynamic programming mới nhận ra chân lí là tìm cách giải brute force trước rồi tìm cách convert qua topdown. Xưa cứ lao đầu vào tìm cách giải topdown + bottom up trước ngáo vl :cautious:
Cách gỉai kiểu này hợp lý cho newbie rồi :big_smile:
Làm quen rồi thì mình sẽ nghĩ cách giải là nó sẽ đệ quy như thế nào, sử dụng memoization thế nào để tránh tính lại những phép toán đã tính rồi :big_smile:
 
Straightforward: Với mỗi đồng xu, ta sẽ chạy vòng lặp từ giá trị đồng xu đó coin đến giá trị cần tìm amount. Mỗi 1 giá trị i thì sẽ cộng thêm 1 khoảng dp[i-coin]. Để đạt được amount -> dp[amount]


JavaScript:
function change(amount: number, coins: number[]): number {
    const dp: number[] = new Array(amount + 1).fill(0);
    dp[0] = 1;
    for (const coin of coins) {
        for (let i = coin; i <= amount; i++) {
            dp[i] += dp[i - coin];
        }
    }
    return dp[amount];
}
Thực ra là cũng éo straightforward, cũng là nghĩ từ DP Bottom-up đi ra :shame:
Cách này hay rồi fence, tối ưu được space complexity :sweet_kiss: mà làm ko quen chắc nhìn ko ra :((

via theNEXTvoz for iPhone
 
JavaScript:
var change = function(amount, coins) {
    let now = [1, ...Array(amount).fill(0)];
    for (const c of coins) {
        for (let i = 0; i <= amount; i++) {
            now[i] += i - c >= 0 ? now[i-c] : 0;
        }
    }
    return now[amount];
};
 
Mã:
func change(amount int, coins []int) int {
    dp := make([]int,amount+1)
    dp[0] = 1
    
    for _,coin := range coins {
        for i := coin; i <= amount; i++ {
            dp[i] += dp[i-coin]
        }
    }
    
    return dp[amount]
}
từng gặp kiểu coin này :rolleyes::rolleyes:
 
Mãi mới có 1 bài dp để quẩy :v
Java:
class Solution {
    public int change(int amount, int[] coins) {
        int[] dp = new int[amount + 1];
        dp[0] = 1;
        for (int coin : coins) {
            for (int i = coin; i <= amount; i++) {
                dp[i] += dp[i-coin];
            }
        }
        return dp[amount];
    }
}
 
Ý tưởng quy hoạch động:
số cách tạo thành amount từ n đồng xu = (số cách tạo amount từ n-1 đồng còn lại (bỏ qua đồng hiện tại) + với số cách tạo amount nếu dùng đồng xu hiện tại)
=> hàm dp(amount, i) = dp(amount, i-1) + dp(amount-coins{i], i)
Nhận thấy thằng dp(, i) được tính từ dp(, i-1) nên có thể làm giống bác anold sử dụng 1 mảng dp 1 chiều thay vì 2 chiều amount * len(coins) giống em.



Python:
class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        @cache
        def solve(amount, i):
            if amount == 0: return 1
            if i < 0 or amount < 0: return 0
            return solve(amount, i-1) + solve(amount-coins[i], i)
        return solve(amount, len(coins)-1)
 
Java:
class Solution {
    fun change(amount: Int, coins: IntArray): Int {
        val dp = IntArray(amount + 1)
        dp[0] = 1

        for (coin in coins) {
            for (k in coin..amount) {
                dp[k] += dp[k - coin]
            }
        }

        return dp[amount]
    }
}
 
Như bài hôm nay chả hạn my fence.
Đầu tiên mình vẽ decision tree rồi code brute force
Xem tệp đính kèm 2008383

C#:
public class Solution {
    private int result = 0;
    private int[] coins;
    public int Change(int amount, int[] coins) {
         this.coins = coins;
         NumFlips(0, amount);
         return result;
    }

    private void NumFlips(int index, int amount)
    {
        if(amount == 0) {
            this.result ++;
            return;
        }

        if(amount < 0 || index >= coins.Length)
            return;

        NumFlips(index, amount - coins[index]);
        NumFlips(index + 1, amount);
    }
}
Chạy kiểm chứng kết quả, ra ngay TLE.
Sửa lại 1 tí từ Brute force qua topdown
C#:
public class Solution {
    private int[] coins;
    public int Change(int amount, int[] coins) {
         this.coins = coins;
         var dp = new int[coins.Length + 1, amount + 1];
         for(int i = 0; i<= coins.Length; i++)
         {
             for(int j = 0; j <= amount; j++)
             {
                 dp[i,j] = -1;
             }
         }
         return NumFlips(0, amount, dp);
    }

    private int NumFlips(int index, int amount, int[,] dp)
    {
        if(amount == 0) {
            return 1;
        }

        if(amount < 0 || index >= coins.Length)
            return 0;

        if(dp[index, amount] == -1)
        {
            dp[index, amount] = NumFlips(index, amount - coins[index], dp) + NumFlips(index + 1, amount, dp);
        }

        return dp[index,amount];
    }
}
Chiến lược quá ok cho newbie như mình :sure: làm thành 2D DP như thật. Chắc cũng sẽ tìm ra được bottom up từ topdown nhưng mà cứ từng bước từ từ đã. Chính thức cán mốc 200 bài code thiếu nhi :doubt: thời gian tổng cộng code bài này chưa tới 15ph. Ko tìm ra topdown thì chắc cũng ko fail PV được :ah:

Tôi thì cũng gần giống thím, đầu tiên là làm đệ quy trước (cách brute force của thím), sau đó thì thấy hàm NumsFlips() lặp nhiều lần => tạo dictionary lưu cái (index, amount) kia là xong. Dĩ nhiên là không tối ưu, nhưng nhẹ cái đầu :sweet_kiss:

C#:
public class Solution {
    Dictionary<(int, int), int> memo = new ();

    public int Change(int amount, int[] coins) {
        return exchange(amount, coins, coins.Length - 1);
    }

    int exchange(int amount, int[] coins, int index)
    {
        if (amount < 0 || index < 0)
            return 0;
        if (amount == 0)
            return 1;
        if (!memo.ContainsKey((amount, index)))
        {
            int take = exchange(amount-coins[index], coins, index);
            int notTake = exchange(amount, coins, index-1);
            memo[(amount, index)] = take + notTake;
        }
        return memo[(amount, index)];
    }
}
 
Tôi thì cũng gần giống thím, đầu tiên là làm đệ quy trước (cách brute force của thím), sau đó thì thấy hàm NumsFlips() lặp nhiều lần => tạo dictionary lưu cái (index, amount) kia là xong. Dĩ nhiên là không tối ưu, nhưng nhẹ cái đầu :sweet_kiss:

C#:
public class Solution {
    Dictionary<(int, int), int> memo = new ();

    public int Change(int amount, int[] coins) {
        return exchange(amount, coins, coins.Length - 1);
    }

    int exchange(int amount, int[] coins, int index)
    {
        if (amount < 0 || index < 0)
            return 0;
        if (amount == 0)
            return 1;
        if (!memo.ContainsKey((amount, index)))
        {
            int take = exchange(amount-coins[index], coins, index);
            int notTake = exchange(amount, coins, index-1);
            memo[(amount, index)] = take + notTake;
        }
        return memo[(amount, index)];
    }
}
Mình thấy nhiều bài dạng này lắm
https://leetcode.com/problems/palindromic-substrings/
https://leetcode.com/problems/coin-change/
https://leetcode.com/problems/decode-ways/solutions/
https://leetcode.com/problems/palindromic-substrings/
Cứ recursion chuyển qua topdown mà quẩy là ngon :D Mấy bài nằm trong top blind 75 của big tech đều dạng này.
Mà bất ngờ là big tech nó ko bao giờ hỏi DP mà bài hard cả. Chắc ko nên dành nhiều thời gian cho bài hard quá đâu. Cứ bài dễ + medium mà tập luyện thôi các fence. Lỡ xui dính bài hard thì coi như tụi nó ko muốn tuyển mình đi :ah:
 
Mình thấy nhiều bài dạng này lắm
https://leetcode.com/problems/palindromic-substrings/
https://leetcode.com/problems/coin-change/
https://leetcode.com/problems/decode-ways/solutions/
https://leetcode.com/problems/palindromic-substrings/
Cứ recursion chuyển qua topdown mà quẩy là ngon :D Mấy bài nằm trong top blind 75 của big tech đều dạng này.
Mà bất ngờ là big tech nó ko bao giờ hỏi DP mà bài hard cả. Chắc ko nên dành nhiều thời gian cho bài hard quá đâu. Cứ bài dễ + medium mà tập luyện thôi các fence. Lỡ xui dính bài hard thì coi như tụi nó ko muốn tuyển mình đi :ah:
True, hard chắc có nước học thuộc hoặc dân CP :v

via theNEXTvoz for iPhone
 
Cũng ko hẳn đến thế đâu thím, làm lâu thì quen thôi. Trừ khi nó động đến kiến thức toán học quá nhiều thì chịu hẳn :big_smile:
Đợt trước cứ cuối tuần là 2-3 bài Hard, làm dần là quen guồng ngay kk

Mịa cuối tuần chỉ muốn vào làm nhanh để còn đi cafe giải trí, bỏ thì mất streak, mà thấy toàn bài hard. Chắc tụi leetcode nghĩ anh em toàn dân hardcore ngốn hết mấy ngày cuối tuần để làm.
 
Bai nay dung bottom-up DP se chay nhanh hon va tot hon ve space complexity. Day la top-down DP :)))
C++:
class Solution {
public:
    int change(int amount, vector<int>& coins) {
        int n = coins.size();
        vector dp(n, vector<int>(amount, -1));
        function<int(int, int)> solve = [&](int i, int s){
            if (s == amount) return 1;
            if (i == n || s > amount) return 0;
            if (dp[i][s] != -1) return dp[i][s];

            return dp[i][s] = solve(i, s + coins[i]) + solve(i + 1, s);
        };
        return solve(0, 0);
    }
};
 
JavaScript:
var uniquePathsWithObstacles = function(obstacleGrid) {
    const m = obstacleGrid.length,
          n = obstacleGrid[0].length;
    let ans = obstacleGrid.map(r => r.map(() => 0));
    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
            if (obstacleGrid[i][j] === 1) {
                continue;
            }
            if (!i && !j) {
                ans[i][j] = 1;
            } else {
                if (i > 0) {
                    ans[i][j] += ans[i-1][j];
                }
                if (j > 0) {
                    ans[i][j] += ans[i][j-1];
                }
            }
        }
    }
    
    return ans[m-1][n-1];
};
 
Trạng thái
Không mở để trả lời thêm.

Thống kê chủ đề

Ngày tạo
_Gia_Cat_Luong_,
Người trả lời cuối
Vipluckystar,
Trả lời
17.755
Lượt xem
1.212.535
Quay lại
Lên đầu trang