thảo luận [Học Tập] Topic thuật toán

  • Người tạo chủ đề Người tạo chủ đề unknowpc90
  • Ngày bắt đầu Ngày bắt đầu
Độ khó: medium

Đề bài: cho một ma trận m * n chỉ mang các giá trị 0 và 1. Hãy trả về một ma trận chứa các giá trị là khoảng cách gần nhất từ các phần tử mang giá trị 1 đến một phần tử gần nhất mang giá trị 0. Quy ước khoảng cách giữa 2 phần tử liền kề nhau là 1

Ví dụ:
Input:
mat = [[0,0,0],[0,1,0],[0,0,0]]
Output: [[0,0,0],[0,1,0],[0,0,0]]
giải thích: khoảng cách các phần tử mang giá trị 0 đến giá trị 0 gần nhất là 0
Khoảng cách từ phần tử 1 ở mat[2][2] cho đến phần tử 0 gần nhất là 1

Giới hạn:
m = mat.size()
n = mat[0].size()
1 <= m, n <= 10^4
1 <= m * n <= 10^4
Có ít nhất 1 phần tử mang giá trị 0

https://leetcode.com/problems/01-matrix/

C++:
class Solution {
public:
    vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
        // Giải bằng quy hoạch động
        if(mat.size() == 0) return mat; // Nếu mat rỗng, trả về mat
        // Khởi tạo ma trận chứa kết quả, khoảng cách ban đầu = không xác định (INT_MAX - 1)
        vector<vector<int>> result(mat.size(), vector<int>(mat[0].size(), INT_MAX - 1));
        for(int i = 0; i < mat.size(); i++){
            for(int j = 0; j < mat[0].size(); j++)
            {
                if(mat[i][j] == 0)
                {
                    result[i][j] = 0; //Khoảng cách từ phần tử 0 đến phần tử 0 gần nhất = 0
                }
                if(i > 0) // Kiểm tra coi có tồn tại phần tử kề trên đầu không
                {
                    // So sánh khoảng cách đang chứa với khoảng cách đến phần tử trên đầu
                    int nearest = std::min(result[i][j], result[i - 1][j] + 1);
                    result[i][j] = nearest;
                }
                 if(j > 0) //Kiểm tra coi có phần tử kề bên trái không
                {
                     // So sánh khoảng cách đến phần tử kề bên trái với khoảng cách đang chứa
                    int nearest = std::min(result[i][j], result[i][j - 1] + 1);
                    result[i][j] = nearest;
                }
            }
        }
        for(int i = mat.size() - 1; i >= 0 ; i--) // Loop thêm 1 vòng nữa. Nhưng lần này là loop ngược lại
        {
            for(int j = mat[0].size() - 1; j >= 0; j--)
            {
                if(i < mat.size() - 1) // So sánh phần tử kề ở dưới
                {
                    int nearest = min(result[i + 1][j] + 1, result[i][j]);
                    result[i][j] = nearest;
                }
                if(j < mat[0].size() - 1) // So sánh phần tử kề bên phải
                {
                    int nearest = min(result[i][j], result[i][j + 1] + 1);
                    result[i][j] = nearest;
                }
            }
        }
        return result;
    }
};
 
Chuỗi 2 và 3 theo rule gì? Lỡ có 2, con đường khác nhau (có thể tương đương về số bước hoặc không thì sao)
Bài này cũng quá mức cơ bản, chỉ cần BFS dùng queue để implement là được, mỗi element trong queue có thể chứa điểm (x, y) và điểm trứơc đó (xp, yp)

Sent from Sony H8296 using vozFApp
 
Chuỗi 2 và 3 theo rule gì? Lỡ có 2, con đường khác nhau (có thể tương đương về số bước hoặc không thì sao)
Bài này cũng quá mức cơ bản, chỉ cần BFS dùng queue để implement là được, mỗi element trong queue có thể chứa điểm (x, y) và điểm trứơc đó (xp, yp)

Sent from Sony H8296 using vozFApp
:nosebleed: Không hiểu ý bác lắm. Chính vì có tối đa 4 con đường khác nhau nên phải xét cả đường ở trên, đường ở dưới, đường bên trái và đường bên phải
Về số bước thì ví dụ như ta có mảng theo thứ tự ô là A, B, C, D
khoảng cách AC chả phải = AB + BC à? Tương tự khoảng cách AD = AC + CD thì đó chẳng phải là hệ thức truy hồi trong quy hoạch động sao? Liên quan gì đến việc tương đương số bước? Ta có ma trận vừa là kết quả vừa là ô chứa các "khoảng cách các phần tử trước" thì quy hoạch động dễ rồi
Mà tính ra đúng là bài này dùng BFS được nhưng quy hoạch động thì nó lại dễ hiểu hơn, theo ý kiến cá nhân của tôi thôi.
 
:nosebleed: Không hiểu ý bác lắm. Chính vì có tối đa 4 con đường khác nhau nên phải xét cả đường ở trên, đường ở dưới, đường bên trái và đường bên phải
Về số bước thì ví dụ như ta có mảng theo thứ tự ô là A, B, C, D
khoảng cách AC chả phải = AB + BC à? Tương tự khoảng cách AD = AC + CD thì đó chẳng phải là hệ thức truy hồi trong quy hoạch động sao? Liên quan gì đến việc tương đương số bước? Ta có ma trận vừa là kết quả vừa là ô chứa các "khoảng cách các phần tử trước" thì quy hoạch động dễ rồi
Mà tính ra đúng là bài này dùng BFS được nhưng quy hoạch động thì nó lại dễ hiểu hơn, theo ý kiến cá nhân của tôi thôi.
vấn đề là nếu bạn dùng quy hoạch động thì bạn phải xác định và giải được bài toán con trước. Trong bài này thì bài toán con tại 1 ô bất kỳ chính là 4 ô xung quanh. vd:
a, b, c
d, e, f
m, n, i
=> dis(e) = min(dis(b), dis(d), dis(f), dis(n)) + 1
=> muốn tính khoảng cách tại e thì cần tính khoảng cách tại b,d,f,n (1)
mà dis(b) = min(dis(e), dis(a), dis(c)) + 1
Đến đây ta thấy muốn tính được dis(b) thì lại cần tính dis(e). (2)
(1) (2) => muốn tính dis(e) thì cần tính dis(b). muốn tính dis(b) thì lại cần tính dis(e) => nếu không phân rã được mối quan hệ này thì sẽ không giải được theo cách này.

Do đó mình thấy bài này nên dùng bfs bt thôi. Đây là cách phân tích của mình, nếu bạn có cách khác để giải theo quy hoạch động thì chia sẻ cho mọi người biết với.
 
vấn đề là nếu bạn dùng quy hoạch động thì bạn phải xác định và giải được bài toán con trước. Trong bài này thì bài toán con tại 1 ô bất kỳ chính là 4 ô xung quanh. vd:
a, b, c
d, e, f
m, n, i
=> dis(e) = min(dis(b), dis(d), dis(f), dis(n)) + 1
=> muốn tính khoảng cách tại e thì cần tính khoảng cách tại b,d,f,n (1)
mà dis(b) = min(dis(e), dis(a), dis(c)) + 1
Đến đây ta thấy muốn tính được dis(b) thì lại cần tính dis(e). (2)
(1) (2) => muốn tính dis(e) thì cần tính dis(b). muốn tính dis(b) thì lại cần tính dis(e) => nếu không phân rã được mối quan hệ này thì sẽ không giải được theo cách này.

Do đó mình thấy bài này nên dùng bfs bt thôi. Đây là cách phân tích của mình, nếu bạn có cách khác để giải theo quy hoạch động thì chia sẻ cho mọi người biết với.
Để phân rã được mối quan hệ này thì ta phải chọn đâu là hạng tử đầu của hệ thức truy hồi
Cụ thể theo như ví dụ của bác thì đầu tiên ta sẽ xét phần tử b trước. Nếu b = 0 thì ta cũng sẽ có dis e, ngược lại nếu b không xác định thì ta sẽ xét phần tử e. Nếu e vẫn không xác định thì ta cũng sẽ xét khoảng cách các phần tử kế e TRỪ b,...cho tới khi ta đụng một phần tử = 0 -> cũng là điều mà theo đề bài thì ta chắc chắn sẽ có ít nhất 1 phần tử = 0
Điểm mấu chốt ở đây là nếu b xác định thì e cũng sẽ xác định, ngược lại b không xác định thì e sẽ xác định, e không xác định thì các phần tử khác xác định. Ở đây giống như là một hàm đệ quy khi ta cứ liên tục xét 4 phía của 4 phía phần tử ban đầu cho tới khi đụng phải số 0 thì dừng
 
vấn đề là nếu bạn dùng quy hoạch động thì bạn phải xác định và giải được bài toán con trước. Trong bài này thì bài toán con tại 1 ô bất kỳ chính là 4 ô xung quanh. vd:
a, b, c
d, e, f
m, n, i
=> dis(e) = min(dis(b), dis(d), dis(f), dis(n)) + 1
=> muốn tính khoảng cách tại e thì cần tính khoảng cách tại b,d,f,n (1)
mà dis(b) = min(dis(e), dis(a), dis(c)) + 1
Đến đây ta thấy muốn tính được dis(b) thì lại cần tính dis(e). (2)
(1) (2) => muốn tính dis(e) thì cần tính dis(b). muốn tính dis(b) thì lại cần tính dis(e) => nếu không phân rã được mối quan hệ này thì sẽ không giải được theo cách này.

Do đó mình thấy bài này nên dùng bfs bt thôi. Đây là cách phân tích của mình, nếu bạn có cách khác để giải theo quy hoạch động thì chia sẻ cho mọi người biết với.
:nosebleed: Với cả bài giải bằng dynamic programming mình có bỏ vào phần spoiler rồi mà bác?
 
:nosebleed: Với cả bài giải bằng dynamic programming mình có bỏ vào phần spoiler rồi mà bác?
Sorry, nãy nhìn k kỹ. T hiểu được ý tưởng này rồi. Độ phức tạp của cách này với bfs đều là O(n^2). Tuy nhiên cách này có ưu điểm là access cái array tuần tự, nên sẽ ít bị miss cache hơn cách dùng bfs. Do đó nó sẽ chạy nhanh hơn. :D
 
Sorry, nãy nhìn k kỹ. T hiểu được ý tưởng này rồi. Độ phức tạp của cách này với bfs đều là O(n^2). Tuy nhiên cách này có ưu điểm là access cái array tuần tự, nên sẽ ít bị miss cache hơn cách dùng bfs. Do đó nó sẽ chạy nhanh hơn. :D
Miss cache là sao nhỉ. Bài này đẩy hết các phần tử 0 ban đầu vào queue rồi loang thôi :sweat::sweat:

via theNEXTvoz for iPhone
 
Miss cache là sao nhỉ. Bài này đẩy hết các phần tử 0 ban đầu vào queue rồi loang thôi :sweat::sweat:

via theNEXTvoz for iPhone
Cách bạn nói k sai. Chỉ có điều cách đó sẽ thường access không tuần tự, mà tùy thuộc vào vị trí của các phần tử 0 ban đầu.
Còn cách dùng quy hoạch động như ở #968 thì sẽ access array một cách tuần tự (partial locality) => ít bị miss cache => chạy nhanh hơn.
Nếu đến đây bạn vẫn chưa hiểu thì có thể tìm hiểu cơ chế hoạt động của CPU cache, cache-friendly code.
 
thread chất lượng quá, mod rãnh rỗi stick thread này lên box cntt luôn đc ko

mấy thím code nhiều đến mức quen hết patern , nhưng cho em hỏi cái patern này có thực sự áp dụng vào công việc thực tế đc ko vậy, em thấy làm nhiều nó tạo cho mình những lối mòn, những atomic code,như kiểu hồi làm toán phân tích đề tìm giấu hiệu rồi đưa về dạng đã giải đc mà giải thôi ấy, chứ đổ nhiều thời gian vào mà mục đích chỉ để phỏng vấn thì hơi phí phạm
 
thread chất lượng quá, mod rãnh rỗi stick thread này lên box cntt luôn đc ko

mấy thím code nhiều đến mức quen hết patern , nhưng cho em hỏi cái patern này có thực sự áp dụng vào công việc thực tế đc ko vậy, em thấy làm nhiều nó tạo cho mình những lối mòn, những atomic code,như kiểu hồi làm toán phân tích đề tìm giấu hiệu rồi đưa về dạng đã giải đc mà giải thôi ấy, chứ đổ nhiều thời gian vào mà mục đích chỉ để phỏng vấn thì hơi phí phạm
Nó rèn luyện tư duy của mình, biết cách để giải quyết vấn đề, còn học vẹt theo dạng thì cũng chỉ để đối phó mà thôi.
 
Cái này bt mà. Chủ yếu là luyện về cách tiếp cận, giải quyết bài toán thôi. Chứ đâu ai luyện để nhớ đề làm gì. :D .
Mà chuyện đọc lại code của mình không hiểu cũng là điều bình thường. Nhiều khi còn nghĩ sao ngày xưa mình code gà vậy, :beat_brick:. Nhưng việc thấy code lúc trước của mình cùi bắp cũng là một dấu hiệu tốt, chứng tỏ mình đã tiến bộ. :D
Ngày xưa làm dự án cho công ty toàn fix bug của người đi trước nên nghĩ nó quên cũng ko khó hiểu lắm, sau làm cho mình cũng quên là sao ta
 
thread chất lượng quá, mod rãnh rỗi stick thread này lên box cntt luôn đc ko

mấy thím code nhiều đến mức quen hết patern , nhưng cho em hỏi cái patern này có thực sự áp dụng vào công việc thực tế đc ko vậy, em thấy làm nhiều nó tạo cho mình những lối mòn, những atomic code,như kiểu hồi làm toán phân tích đề tìm giấu hiệu rồi đưa về dạng đã giải đc mà giải thôi ấy, chứ đổ nhiều thời gian vào mà mục đích chỉ để phỏng vấn thì hơi phí phạm
Thím cứ nghĩ nó như một cách để rèn luyện khả năng tư duy giải quyết vấn đề (cũng như thím đi tập gym để rèn luyện thể chất), chứ những kiến thức này rất hiếm khi phải dùng khi đi làm thực tế (trừ khi thím làm chuyên sau về phần thuật toán).
 
Ngày xưa làm dự án cho công ty toàn fix bug của người đi trước nên nghĩ nó quên cũng ko khó hiểu lắm, sau làm cho mình cũng quên là sao ta
Đâu ai nhớ được code đâu bạn. Cùng lắm chỉ nhớ ý tưởng, cách tiếp cận vấn đề thôi. Hơn nữa chuyện quên code của mình là tốt ấy chứ. Vì khi đó mình sẽ giống như một người khác đọc lại code của mình và mình sẽ dễ thấy được những chỗ mình làm chưa tốt. :D
 
Thắc mắc lưu âm lịch vào csdl!
Tình hình là em đang làm đồ án nho nhỏ cá nhân, đến phần lưu sự kiện, dính ngày Giỗ Tổ và Tết tính theo âm lịch. Cái bảng nó như này
Mã:
Events {
    int event_id
    string event_name
    date  start
    date end    
}
Em định thêm 1 column nữa oánh dấu cái nào ngày dương (+) cái nào ngày âm (-). Lúc đọc dữ liệu thì code sẽ xử lý tính ngày dựa vào cột này. Vậy có ổn ko ạ? :pudency:
 
Thắc mắc lưu âm lịch vào csdl!
Tình hình là em đang làm đồ án nho nhỏ cá nhân, đến phần lưu sự kiện, dính ngày Giỗ Tổ và Tết tính theo âm lịch. Cái bảng nó như này
Mã:
Events {
    int event_id
    string event_name
    date  start
    date end   
}
Em định thêm 1 column nữa oánh dấu cái nào ngày dương (+) cái nào ngày âm (-). Lúc đọc dữ liệu thì code sẽ xử lý tính ngày dựa vào cột này. Vậy có ổn ko ạ? :pudency:
T nghĩ cách này không ổn. Vì khi đó bạn sẽ đẩy phần logic chuyển đổi ngày âm, dương sang code.
Theo t thì bạn nên thống nhất chỉ lưu ngày âm hoặc dương thôi (theo quan điểm cá nhân thì nên là ngày dương). Khi đó bạn chỉ cần chuyển đổi trước khi import vào db. Còn khi query thì k cần nữa. Như vậy sẽ dễ kiểm soát logic hơn.
 
T nghĩ cách này không ổn. Vì khi đó bạn sẽ đẩy phần logic chuyển đổi ngày âm, dương sang code.
Theo t thì bạn nên thống nhất chỉ lưu ngày âm hoặc dương thôi (theo quan điểm cá nhân thì nên là ngày dương). Khi đó bạn chỉ cần chuyển đổi trước khi import vào db. Còn khi query thì k cần nữa. Như vậy sẽ dễ kiểm soát logic hơn.
Nhưng vì ngày âm thay đổi theo năm, ko cố định, nếu là lưu dưới dạn ngày dương thì lại phải thay đổi liên tục thông tin sau mỗi năm :sad:
 
Nhưng vì ngày âm thay đổi theo năm, ko cố định, nếu là lưu dưới dạn ngày dương thì lại phải thay đổi liên tục thông tin sau mỗi năm :sad:
Thì bản chất nó là như vậy mà. Nếu không chuyển đổi từ khi import vào thì bạn cũng sẽ phải chuyển đổi tại chỗ query thôi. Khi đó nếu có nhiều chỗ query thì bạn sẽ khó kiểm soát hơn.
Bạn có thể import vào khoảng 10 năm. 10 năm sau import tiếp, :big_smile:
 
Xin hướng dẫn bài này,
https://codeforces.com/gym/101933/problem/K
tóm tắt: bài này tìm số cách tô màu một cây n node bằng k màu, sao cho 2 node nằm cùng trên 1 cạnh khác màu.
QHĐ thôi, có thêm 1 param để lưu lại số màu chưa dùng: f(index, colorLeft)
Để tối về tôi code thử xem có đúng ko.

Edit: Accepted rồi, bài này ko hiểu cho tree vào làm j, đúng là cú lừa 🤔
 
Sửa lần cuối:

Thống kê chủ đề

Ngày tạo
unknowpc90,
Người trả lời cuối
Spaghetti Code,
Trả lời
1.460
Lượt xem
154.138
Quay lại
Lên đầu trang