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
Mấy thím cho em hỏi học thuật toán ngoài phòng vấn ra thì ra làm có áp dụng nhiều không ạ.
Công ty lớn nó test thuật toán vì ngoài kĩ năng lập trình còn test fen khả năng giải quyết vấn đề của fen ( cái mà ltv gặp hàng ngày ), mình biết có ông từng apply công ty vào FAANG ( Netflix ) éo giải ra bài nào luôn vẫn đậu. Vì sao ? vì trong lúc làm ổng làm ổng đặt câu hỏi đúng, tương tác với giám khảo tốt, đặt ra định hương giải quyết vấn đề tốt ( dù không giải ra kịp thời gian ). Còn có ông lầm lầm lì lì giải đúng hết tất cả các bài luôn vẫn rớt nhé
janDexM.jpg
 
Sửa lần cuối:
Sau 1 hồi suy nghĩ thì em hiểu bài toán như này
Xem tệp đính kèm 716362
Chính xác luôn mai fen. Bài này bản chất là bài LIS nhưng limit len = 3 thôi. Có thím Thu Nga ở trên giải bằng LIS đó, stop early phát là hết TLE ngay.

Với cá nhân mình cảm nhận là các cty top tier nó tuyển LTV biết và hiểu thuật toán ko phải là nó thực sự cần thuật toán đâu. Mà là vì những người LTV biết thuật toán họ điều có 1 điểm chung đó là sự chăm chỉ. Nhưng trong cái topic này cũng vậy thôi. Toàn những người chăm chỉ giải 1 toán, nghiên cứu những kiến thức mới, học thêm cái hay. Thời gian rảnh thì lôi thuật toán ra giải để ko bị lãng phí time.
Mà mấy đức tính đó thì chỉ có những LTV biết thuật toán mới có được. Nên mình nghĩ đó chính là lý do vì sao top tier nó tuyển LTV giỏi thuật toán.
Bác @_Gia_Cat_Luong_ là 1 ví dụ điển hình nè. Chắc bác ấy giờ vào được Axon màu vàng rồi ấy chứ. Nên ko còn time chia sẻ với anh em nữa =((
Axon màu vàng cty nào v fen ? Amazon à ?
Mình thấy stop vì muốn spend more time để học tiếng anh thôi, chứ đã apply mịa gì đâu T_T.
 
Chính xác luôn mai fen. Bài này bản chất là bài LIS nhưng limit len = 3 thôi. Có thím Thu Nga ở trên giải bằng LIS đó, stop early phát là hết TLE ngay.


Axon màu vàng cty nào v fen ? Amazon à ?
Mình thấy stop vì muốn spend more time để học tiếng anh thôi, chứ đã apply mịa gì đâu T_T.
Nếu bác ở HCM thì search Axon quận 4 là ra à. Tại có tới 2 3 công ty gì đó đều tên là Axon mà riêng Axon ở quận 4 là ngon nhất và có logo là màu vàng nên gọi là Axon màu vàng.
Trình bác apply vào đó thì lương khởi điểm phải nói là mút mùa.
Nghe bảo pv thuật toán cũng toàn mấy bài trên leetcode chủ yếu là medium và hard.
Nếu bác ở đâu đó mà ko phải SG thì cứ mạnh dạn vào SG apply bọn Axon màu vàng này. Lương dư sức trả tiền thuê phòng ở chung cư vinhomes luôn.
 
Nếu bác ở HCM thì search Axon quận 4 là ra à. Tại có tới 2 3 công ty gì đó đều tên là Axon mà riêng Axon ở quận 4 là ngon nhất và có logo là màu vàng nên gọi là Axon màu vàng.
Trình bác apply vào đó thì lương khởi điểm phải nói là mút mùa.
Nghe bảo pv thuật toán cũng toàn mấy bài trên leetcode chủ yếu là medium và hard.
Nếu bác ở đâu đó mà ko phải SG thì cứ mạnh dạn vào SG apply bọn Axon màu vàng này. Lương dư sức trả tiền thuê phòng ở chung cư vinhomes luôn.
Nghe nói lương bên đó cao lắm. :p
Mà đi phỏng vấn nhiều khi cũng hên xui lắm. Nhiều khi còn phụ thuộc vào tâm trạng ng phỏng vấn nữa. :D
 
Mấy thím cho em hỏi học thuật toán ngoài phòng vấn ra thì ra làm có áp dụng nhiều không ạ.

Mình đi làm được 2 exp và trả lời với bạn dựa trên kinh nghiệm cá nhân là CÓ nhưng rất ÍT.
Với công việc hiện tại của mình, mình chỉ áp dụng thuật toán nói chung và kỹ thuật cá nhân nói riêng vào công việc khi và chỉ khi mình đụng tới phần export file excel với hơn 100k dòng thì lúc đó mình mới dùng. VÌ nếu ko dùng vài kỹ thuật vào chỗ này thì server bị tràn ram, ko xử lý nỗi.
Còn ngoài ra thì hầu như ko cần. Lâu lâu mình giải được các bài toán hay hay thì mình xem rồi áp dụng vô project hiện tại. Vậy thôi

Với cá nhân mình cảm nhận là các cty top tier nó tuyển LTV biết và hiểu thuật toán ko phải là nó thực sự cần thuật toán đâu. Mà là vì những người LTV biết thuật toán họ điều có 1 điểm chung đó là sự chăm chỉ. Nhưng trong cái topic này cũng vậy thôi. Toàn những người chăm chỉ giải 1 toán, nghiên cứu những kiến thức mới, học thêm cái hay. Thời gian rảnh thì lôi thuật toán ra giải để ko bị lãng phí time.
Mà mấy đức tính đó thì chỉ có những LTV biết thuật toán mới có được. Nên mình nghĩ đó chính là lý do vì sao top tier nó tuyển LTV giỏi thuật toán.
Bác @_Gia_Cat_Luong_ là 1 ví dụ điển hình nè. Chắc bác ấy giờ vào được Axon màu vàng rồi ấy chứ. Nên ko còn time chia sẻ với anh em nữa =((
Nói chung có dùng hay không, dùng cái gì thì còn tùy thuộc vào tính chất công việc nữa. Như công việc của t hiện tại là dùng rất nhiều. Tại vì công việc của t liên quan đến data, chủ yếu là ETL. Nên thường cần phải tối ưu về performance lẫn resource. Tất nhiên không dùng đến mấy thuật toán quá phức tạp như những challenge trên leetcode.
 
Nói chung có dùng hay không, dùng cái gì thì còn tùy thuộc vào tính chất công việc nữa. Như công việc của t hiện tại là dùng rất nhiều. Tại vì công việc của t liên quan đến data, chủ yếu là ETL. Nên thường cần phải tối ưu về performance lẫn resource. Tất nhiên không dùng đến mấy thuật toán quá phức tạp như những challenge trên leetcode.
Bác làm DE hả, mình chuẩn bị vô fresher vị trí này, cho mình hỏi trong thực tế thì ngoài ETL ra thì còn có công việc nào thường xuyên làm nữa k?
 
Bác làm DE hả, mình chuẩn bị vô fresher vị trí này, cho mình hỏi trong thực tế thì ngoài ETL ra thì còn có công việc nào thường xuyên làm nữa k?
Uhm, nhưng mà hệ thống bên t rất lớn. Nên t chỉ làm 1 phần rất nhỏ trong cái pipeline thôi. Nên skillset không giống bên ngoài lắm.
Ngoài ETL ra thì sẽ phải viết script để test input, output tại vì làm data thì k manual test đc. Thường sẽ dùng python, nếu data lớn quá thì dùng spark.
 
Uhm, nhưng mà hệ thống bên t rất lớn. Nên t chỉ làm 1 phần rất nhỏ trong cái pipeline thôi. Nên skillset không giống bên ngoài lắm.
Ngoài ETL ra thì sẽ phải viết script để test input, output tại vì làm data thì k manual test đc. Thường sẽ dùng python, nếu data lớn quá thì dùng spark.
Đang làm đâu v fen ? SG hay HN ?
Ở VN mà bảo có data pipline lớn thì chắc cty cũng nổi tiếng ? Share thêm đc k fen ?
 
Mấy bác cho em hỏi, tại sao 2 thuật toán này, em nháp thì nó giống nhau về bản chất nhưng khi chạy nó lại cho kết quả khác nhau vậy ạ?
đề bài:
QkKHzIx.png

Code đầu của em sử dụng thuật toán Kadane thêm điều kiện là dãy con tăng, giảm (nói chung là gộp từ 2 bài toán dãy con tăng/giảm và dãy con có tổng lớn nhất):
C++:
#include<bits/stdc++.h>
using namespace std;
int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        int a[n+1];
        vector<int>dp1(n+1,0);
        vector<int>dp2(n+1,0);
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        dp1[1]=a[1];
        dp2[n]=a[n];
        int res=0;
        for(int i=1;i<n;i++){
            for(int j=1;j<i;j++){
                if(a[i]>a[j])
            dp1[i]=max(a[i],dp1[j]+a[i]);
        }
        }
        for(int i=n-1;i>=0;i--){
            for(int j=n;j>i;j--){
                if(a[i]>a[j])
                dp2[i]=max(a[i],dp2[j]+a[i]);
            }
        }
        for(int i=1;i<=n;i++){
            res=max(res,dp1[i]+dp2[i]-a[i]);
        }
        cout<<res<<endl;
    }
}
Kết quả 200, 170.
code 2:
C++:
#include<bits/stdc++.h>
using namespace std;
int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        int a[n+1];
        vector<int>dp1(n+1,0);
        vector<int>dp2(n+1,0);
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        dp1[1]=a[1];
        dp2[n]=a[n];
         int res=0;
        for(int i=1;i<n;i++){
            for(int j=1;j<i;j++){
                if(a[i]>a[j])
            dp1[i]=max(dp1[i],dp1[j]+a[i]);
        }
        }
        for(int i=n-1;i>=0;i--){
            for(int j=n;j>i;j--){
                if(a[i]>a[j])
                dp2[i]=max(dp2[i],dp2[j]+a[i]);
            }
        }
        for(int i=1;i<=n;i++){
            res=max(res,dp1[i]+dp2[i]-a[i]);
        }
        cout<<res<<endl;
    }
}
kết quả 210, 194.
Em thấy về bản chất nó giống nhau mà nhỉ? Chỉ khác cái là 1 thằng so sánh trưc tiếp với a, 1 thằng so sánh với dp đã được gán
 

Tệp đính kèm

  • 1629285421006.png
    1629285421006.png
    421,9 KB · Lượt xem: 82
Sửa lần cuối:
^
Bạn hiểu sai rồi, dãy này có thể không liên tiếp nhau, dp(i) là cái tổng lớn nhất tính đến phần tử thứ i, khi bạn cho j chạy từ 1 -> i thì bạn đang tìm thằng lớn nhất trong số các thằng dp(j)+a(i) mà a(j) < a(i), nên phải update như C2 này:
Mã:
dp1[i]=max(dp1[i],dp1[j]+a[i])
Còn nếu dùng cách update như C1:
Mã:
dp1[i]=max(a[i],dp1[j]+a[i])
thì nó chưa chắc đã là thằng tổng con lớn nhất cho đến phần tử thử i được.
 
Mấy bác cho em hỏi, tại sao 2 thuật toán này, em nháp thì nó giống nhau về bản chất nhưng khi chạy nó lại cho kết quả khác nhau vậy ạ?
đề bài:
QkKHzIx.png

Code đầu của em sử dụng thuật toán Kadane thêm điều kiện là dãy con tăng, giảm (nói chung là gộp từ 2 bài toán dãy con tăng/giảm và dãy con có tổng lớn nhất):
C++:
#include<bits/stdc++.h>
using namespace std;
int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        int a[n+1];
        vector<int>dp1(n+1,0);
        vector<int>dp2(n+1,0);
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        dp1[1]=a[1];
        dp2[n]=a[n];
        int res=0;
        for(int i=1;i<n;i++){
            for(int j=1;j<i;j++){
                if(a[i]>a[j])
            dp1[i]=max(a[i],dp1[j]+a[i]);
        }
        }
        for(int i=n-1;i>=0;i--){
            for(int j=n;j>i;j--){
                if(a[i]>a[j])
                dp2[i]=max(a[i],dp2[j]+a[i]);
            }
        }
        for(int i=1;i<=n;i++){
            res=max(res,dp1[i]+dp2[i]-a[i]);
        }
        cout<<res<<endl;
    }
}
Kết quả 200, 170.
code 2:
C++:
#include<bits/stdc++.h>
using namespace std;
int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        int a[n+1];
        vector<int>dp1(n+1,0);
        vector<int>dp2(n+1,0);
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        dp1[1]=a[1];
        dp2[n]=a[n];
         int res=0;
        for(int i=1;i<n;i++){
            for(int j=1;j<i;j++){
                if(a[i]>a[j])
            dp1[i]=max(dp1[i],dp1[j]+a[i]);
        }
        }
        for(int i=n-1;i>=0;i--){
            for(int j=n;j>i;j--){
                if(a[i]>a[j])
                dp2[i]=max(dp2[i],dp2[j]+a[i]);
            }
        }
        for(int i=1;i<=n;i++){
            res=max(res,dp1[i]+dp2[i]-a[i]);
        }
        cout<<res<<endl;
    }
}
kết quả 210, 194.
Em thấy về bản chất nó giống nhau mà nhỉ? Chỉ khác cái là 1 thằng so sánh trưc tiếp với a, 1 thằng so sánh với dp đã được gán
dp1=max(a,dp1[j]+a); Chỗ này sai nhé thím, nếu thím làm vầy nó chỉ gán cho dp1 = max(a, dp1[i-1])
 
^
Bạn hiểu sai rồi, dãy này có thể không liên tiếp nhau, dp(i) là cái tổng lớn nhất tính đến phần tử thứ i, khi bạn cho j chạy từ 1 -> i thì bạn đang tìm thằng lớn nhất trong số các thằng dp(j)+a(i) mà a(j) < a(i), nên phải update như C2 này:
Mã:
dp1[i]=max(dp1[i],dp1[j]+a[i])
Còn nếu dùng cách update như C1:
Mã:
dp1[i]=max(a[i],dp1[j]+a[i])
thì nó chưa chắc đã là thằng tổng con lớn nhất cho đến phần tử thử i được.
dp1=max(a,dp1[j]+a); Chỗ này sai nhé thím, nếu thím làm vầy nó chỉ gán cho dp1 = max(a, dp1[i-1])
Em đã hiểu. Mỗi lần điều kiện if đúng là phải update nó lên để tiếp tục so sánh, nếu không nó sẽ lấy PHẦN TỬ GẦN NÓ NHẤT thỏa mãn điều kiện if để tính dp1.thanks mấy thím
 
https://leetcode.com/problems/perfect-squares/submissions/
Bài làm hôm nay: perfect square.
Tiện thể đang làm đề của trường có bài giống, đề bài:
EZake6f.png

Ý tưởng: Vì cả 2 đề N<=10000; nên khởi tạo 1 mảng a[] lưu toàn bộ các số chính phương từ 1->10000;
Tiếp theo, khởi tạo 1 mảng dp<n+1,10001>;
Đầu tiên, các vị trí dp[chính phương] đổi từ 10001 về 1;
dựa vào các vị trí dp[chính phương], tạo lên mảng dp từ 1 đến n hoàn thiện
Do code rất ngây thơ và ý tưởng rất ngờ nghệch mong các thím đừng cười.
code 1: đpt O(n^2) //tle
C++:
class Solution {
public:
    int numSquares(int n) {
    int a[101];
    for(int i=1;i<=100;i++){
        a[i]=i*i;
    }
    vector<int>dp(n+1,10001);  
    int j=1;
    for(int i=1;i<=100;i++){
        for(int j=1;j<=n;j++){
            if(a[i]==j){
                dp[j]=1;
            }
        }
    }
    dp[0]=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<i;j++){
            dp[i]=min(dp[i],dp[i-j]+dp[j]);
        }
    }
        return dp[n];
    }
};
Tất nhiên là TLE, 10^4^2 cơ mà.
Sau 1 hồi đắn đo nên tìm cách khác hay xài trick để pass, cuối cùng thì chấp nhận xài trick; thay vì chạy j từ 1 đến i, chỉ chạy từ 1 đến sqrt(i);
Code 2:
C++:
class Solution {
public:
    int numSquares(int n) {
    int a[101];
    for(int i=1;i<=100;i++){
        a[i]=i*i;
    }
    vector<int>dp(n+1,10001);  
int j=1;
    for(int i=1;i<=n;i++){
        if(i==a[j]){
            dp[i]=1;
            j++;
        }
    }
    dp[0]=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<sqrt(i);j++){
            dp[i]=min(dp[i],dp[i-j*j]+dp[j*j]);
        }
    }
        return dp[n];
    }
};
Beat 26% submissions, hơi hẻo mà mới học thì AC là thành công rồi. Soi discuss thì không có ai xài cách này, chắc là do nó quá ngây thơ nên chưa ai nghĩ tới.
 
Không chắc lắm nhưng mình nhớ có định lý nói là mọi số tự nhiên đều có thể viết dưới dạng của tổng 4 số chính phương.
Link wiki
Vấn đề là có thể viết dưới dạng của tổng ba hoặc hai số chính phương được. Vậy nên mình cần check xem số nhỏ nhất có thể thôi.
 
https://leetcode.com/problems/perfect-squares/submissions/
Bài làm hôm nay: perfect square.

Code 2:
C++:
class Solution {
public:
    int numSquares(int n) {
    int a[101];
    for(int i=1;i<=100;i++){
        a[i]=i*i;
    }
    vector<int>dp(n+1,10001);
int j=1;
    for(int i=1;i<=n;i++){
        if(i==a[j]){
            dp[i]=1;
            j++;
        }
    }
    dp[0]=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<sqrt(i);j++){
            dp[i]=min(dp[i],dp[i-j*j]+dp[j*j]);
        }
    }
        return dp[n];
    }
};
Beat 26% submissions, hơi hẻo mà mới học thì AC là thành công rồi. Soi discuss thì không có ai xài cách này, chắc là do nó quá ngây thơ nên chưa ai nghĩ tới.
Ý tưởng đúng rồi đấy, code nhiều quen tay thôi. Với code này thì bỏ mảng a đi, không cần thiết là tốc độ tăng ngay :big_smile:
for(int j=1;j<sqrt(i);j++) sửa thành j<=sqrt(i) nhé, hoặc j*j<=i cũng được vì j cũng nhỏ.
 
Nay rảnh ngồi làm mấy bài leetcode, thấy bài này khá hay, nên muốn share lại:
https://leetcode.com/problems/array-of-doubled-pairs/

Input của bài này là 1 dãy có số phần tử là số chẵn. Yêu cầu cần xác định xem mảng đó có thể đc sắp xếp lại để các phần tử trong mảng đi với nhau từng cặp, và số phía sau gấp đôi số phía trước:
vd: [1,2,2,1] -> reorder lại thành [1,2,1,2] -> true
vd: [1,2,2,1,4,1] -> có 3 số 1 mà chỉ có 1 số 2 nên k đủ để bắt cặp cho số 1 -> false
vd: [1,2,2,2,4,1] -> reorder lại thành [1,2,1,2,2,4] -> true

Để giải quyết bài này thì cách đầu tiên mình nghĩ đến là đơn giản duyệt hết các phần tử trong mảng, sau đó search xem có tồn tại phần tử để ghép cặp không bằng cách x2 (và /2 nếu số đó là số chẵn). Tuy nhiên nó sẽ có 1 vấn đề như vd dưới đây:
[4,8,2,16] : khi duyệt đến 4 thì mình lấy 8 để ghép thành 1 cặp, còn lại 2,16 thì không ghép được -> false. Trong khi dãy nãy hoàn toàn có thể được reorder như sau để thỏa mãn yêu cầu của bài toán: [2,4,8,16].

=> mình cần phải sort trước khi duyệt. Cộng với yêu cầu về search nên bài này rất phù hợp để dùng map.

C++:
class Solution {
public:
    bool canReorderDoubled(vector<int>& arr) {
        map<int,int> myMap;
        for (auto x : arr){
            myMap[x]++;
        }
        
        for (auto it : myMap){
            if (it.first == 0 || it.second <= 0) continue;
            auto find_it = it.first > 0 ? myMap.find(it.first*2) : it.first%2==0 ? myMap.find(it.first/2) : myMap.end();
            if (find_it == myMap.end() || find_it->second < it.second) return false;
            find_it->second -= it.second;
        }
        return true;
    }
};

Runtime: 72 ms, faster than 95.72% of C++ online submissions for Array of Doubled Pairs.
Memory Usage: 58 MB, less than 53.38% of C++ online submissions for Array of Doubled Pairs.


Độ phức tạp của solution này là O(NlogN). :D
 
Không chắc lắm nhưng mình nhớ có định lý nói là mọi số tự nhiên đều có thể viết dưới dạng của tổng 4 số chính phương.
Link wiki
Vấn đề là có thể viết dưới dạng của tổng ba hoặc hai số chính phương được. Vậy nên mình cần check xem số nhỏ nhất có thể thôi.
E tưởng viết dưới dạng 1000 số cũng được. Ví dụ 1000=1^2*1000
Ý tưởng đúng rồi đấy, code nhiều quen tay thôi. Với code này thì bỏ mảng a đi, không cần thiết là tốc độ tăng ngay :big_smile:
for(int j=1;j<sqrt(i);j++) sửa thành j<=sqrt(i) nhé, hoặc j*j<=i cũng được vì j cũng nhỏ.
đã fix rồi, thanks bác
 
Nay rảnh ngồi làm mấy bài leetcode, thấy bài này khá hay, nên muốn share lại:
https://leetcode.com/problems/array-of-doubled-pairs/

Input của bài này là 1 dãy có số phần tử là số chẵn. Yêu cầu cần xác định xem mảng đó có thể đc sắp xếp lại để các phần tử trong mảng đi với nhau từng cặp, và số phía sau gấp đôi số phía trước:
vd: [1,2,2,1] -> reorder lại thành [1,2,1,2] -> true
vd: [1,2,2,1,4,1] -> có 3 số 1 mà chỉ có 1 số 2 nên k đủ để bắt cặp cho số 1 -> false
vd: [1,2,2,2,4,1] -> reorder lại thành [1,2,1,2,2,4] -> true

Để giải quyết bài này thì cách đầu tiên mình nghĩ đến là đơn giản duyệt hết các phần tử trong mảng, sau đó search xem có tồn tại phần tử để ghép cặp không bằng cách x2 (và /2 nếu số đó là số chẵn). Tuy nhiên nó sẽ có 1 vấn đề như vd dưới đây:
[4,8,2,16] : khi duyệt đến 4 thì mình lấy 8 để ghép thành 1 cặp, còn lại 2,16 thì không ghép được -> false. Trong khi dãy nãy hoàn toàn có thể được reorder như sau để thỏa mãn yêu cầu của bài toán: [2,4,8,16].

=> mình cần phải sort trước khi duyệt. Cộng với yêu cầu về search nên bài này rất phù hợp để dùng map.

C++:
class Solution {
public:
    bool canReorderDoubled(vector<int>& arr) {
        map<int,int> myMap;
        for (auto x : arr){
            myMap[x]++;
        }
      
        for (auto it : myMap){
            if (it.first == 0 || it.second <= 0) continue;
            auto find_it = it.first > 0 ? myMap.find(it.first*2) : it.first%2==0 ? myMap.find(it.first/2) : myMap.end();
            if (find_it == myMap.end() || find_it->second < it.second) return false;
            find_it->second -= it.second;
        }
        return true;
    }
};

Runtime: 72 ms, faster than 95.72% of C++ online submissions for Array of Doubled Pairs.
Memory Usage: 58 MB, less than 53.38% of C++ online submissions for Array of Doubled Pairs.


Độ phức tạp của solution này là O(NlogN). :D
Map ở C++ là dictionary ở Python hả thím :V, không code C++ nên ko biết
 

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.147
Quay lại
Lên đầu trang