Tác giả: Nguyễn Xuân Khánh
Trong bài viết này mình sẽ cố gắng trả lời ngắn gọn và đơn giản những câu hỏi mà mình nghĩ rằng sẽ có ích đối với các bạn đặc biệt có đam mê đối với Tin học, nhưng mới tiếp xúc và không biết phải bắt đầu từ đâu. Trọng tâm của mình sẽ là việc học thuật toán để tham gia các kì thi quốc gia, tuy nhiên mình cũng sẽ cố gắng gợi mở thêm nhiều hướng khác trong phạm trù kinh nghiệm của mình.
Lưu ý: Xuất xứ của mình là một người học Tin để thi quốc gia, sau đó chuyển dần sang nghiên cứu khoa học về Machine Learning. Vì thế, những gì được viết dưới đây xuất phát từ kinh nghiệm của riêng bản thân mình, và chỉ thể hiện góc nhìn từ con đường mình đã đi. Mình khuyên các bạn nên chừa ra một khoảng trống trong tâm hồn để thu nhặt các ý kiến từ các góc nhìn khác.
Sau đây là các câu hỏi…
Về cơ duyên đến với Tin học của mình, bạn có thể tham khảo bài viết trước. Hy vọng các bạn sẽ tìm thấy điểm chung nào đó. Một điều mình muốn nói thêm là Tin học hiện nay giống như một môn Toán thứ hai vậy. Nếu sau cuộc cách mạng công nghiệp, nhân loại bắt đầu gắn động cơ vào bất cứ mọi thứ xung quanh, thì đến cuộc cách mạng máy tính hiện tại, con người bắt đầu dùng máy tính vào mọi việc có thể. Một công việc trong thế giới hiện đại khó mà có thể vận hành hiệu quả mà không cần dùng đến máy tính. Vì thế, một con người trong thế giới hiện đại khó mà có thể thành công mà không có kỹ năng sử dụng công cụ này. Bạn bè của mình làm nhiều ngành khác nhau, từ toán học, hoá học, vật lý, đến kiểm toán. Họ đều phải chí ít phải có kỹ năng lập trình để phục vụ cho công việc của mình.
Rất khó để đưa được một câu trả lời trọn vẹn. Thay vì đó mình sẽ nói về một cách học mình cho là chưa đúng. Đó là quan niệm học Tin học tức là học một ngôn ngữ lập trình. Các cách nói dân gian như “học Pascal” không thể hiện được hết tinh thần của việc học Tin. Đối với phạm trù Tin học cấp 3, điều quan trọng nhất bạn rút ra được sau 3 năm học phải một tư duy thuật toán. Bạn cần làm cho bộ não quen thuộc với lối suy nghĩ theo các cấu trúc điều kiện, cấu trúc lặp lại, cách chia chương trình ra thành các chương trình nhỏ hơn rồi tập trung giải quyết từng phần một. Vì đa phần các ngôn ngữ lập trình đều được xây dựng dựa trên các yếu tố cơ bản trên, việc bạn thành thạo về tư duy lập trình sẽ giúp bạn học một ngôn ngữ lập trình rất nhanh. Hãy làm chủ ngôn ngữ lập trình chứ đừng bị phụ thuộc vào chúng.
Mình không phản đối việc thành thục một ngôn ngữ lập trình. Thậm chí việc thành thạo một ngôn ngữ lập trình là quan trọng sống còn sau này khi bạn muốn làm ra những sản phẩm thực thụ. Nhưng hãy để việc đó sau khi bạn đã có một nền tảng tư duy vững chắc.
Chính xác. Câu trả lời này mình xin chia ra dành cho hai đối tượng:
Các bạn luyện thi tin học quốc gia: theo mình biết thì hai lựa chọn chính dành cho các bạn là Pascal và C++. Việc đưa C++ vào danh sách là một thay đổi lớn vì 6 năm trước khi mình học Tin, Pascal là sự lựa chọn duy nhất cho các vòng thi trong nước. Trong tương lai mình dự đoán C++ sẽ thay thế hẳn cho Pascal. Mình không rành về Pascal bằng C++ nên sẽ không so sánh tính năng của chúng chi tiết. Tuy nhiên, C++ đưa ra ưu thế rõ rệt về tốc độ, bao gồm cả tốc độ chạy của chương trình và tốc độ lập trình ra chương trình đó. Nói đơn giản là code C++ chạy nhanh và tương đối ngắn gọn (không quá ngắn gọn đến mức khó hiểu). Đặc biệt, khi học C++ bạn có thể tham gia được rất nhiều kỳ thi lập trình thuật toán trực tuyến trên mạng. Đây là mấu chốt trong việc thành công trong kỳ thi quốc gia. Một lợi thế khác khi học C++ là nó được sử dụng rộng rãi trong công nghiệp. Vì sớm muộn gì bạn cũng phải học đến nó chi bằng học sớm từ đầu.
Các bạn học tin với mục đích chung khác: ngoài C++ ra, theo mình ngôn ngữ Python là một sự khởi đầu tuyệt vời. Cú pháp của Python cực kỳ đơn giản, giống như là đang viết những phép toán trong sách giáo khoa vậy. Dù đơn giản, Python lại rất đa năng và được hỗ trợ mạnh mẽ từ cộng đồng người sử dụng. Java là một sự lựa chọn tốt cho những bạn muốn học về lập trình hướng đối tượng một cách bài bản. Dù cả Python và C++ đèu hỗ trợ lập trình hướng đối tượng, Java theo mình là ngôn ngữ biểu hiện điều này rõ rệt nhất. Bạn không thể viết một chương trình hoàn chỉnh trong Java mà không đặt nó vào trong một đối tượng (class), trong khi nếu sử dụng Python và C++ ta có thể quên bẵng đi khái niệm này. Ngôn ngữ Java chặt chẽ và dạy cho bạn những thói quen tốt về cách thiết kế chương trình.
Mình sẽ giả sử là các bạn muốn học C++. Theo mình thì việc chạy ra nhà sách và mua ngay một cuốn sách giáo khoa dày cộm về C++ không giúp ích gì mấy (vì mình đã từng làm điều này). Hồi mình mới học C++, mình thường lên các trang giải bài trực tuyến hoặc cách trang kỳ thi và xem code C++ của các cao thủ khác để học theo cách code của học. Điều này có hai lợi ích. Thứ nhất, vì mình đã biết trước lời giải, nên mình có thể đoán được từng phần chương trình sẽ làm nhiệm vụ gì, sau đó đi sau vào xem cụ thể nhiệm vụ đó được thực hiện thế nào. Thứ hai, vì là cao thủ nên code của họ sẽ rất tối ưu, có thể học được nhiều mẹo vặt mà sách giáo khoa không dạy. Một cách khác là bạn có thể google từ khoá “C++ interactive tutorial” để tìm kiếm cách trang dạy ngôn ngữ lập trình một cách tương tác. Các trang này thường sẽ đưa bạn đi qua các khái niệm từ dễ đến khó. Bạn vừa học vừa thực hành ngay nên sẽ bớt nhàm chám hơn là ngồi cày sách. Tuy nhiên, về lâu về dài bạn vẫn phải đọc sách hoặc tài liệu chính thống để hiểu biết các khái niệm cốt lõi của một ngôn ngữ. Ví dụ như là trong C++ bạn có thể truyền tham số bằng cả reference hoặc value, trong khi đó Java chỉ cho phép truyền tham số bằng value mà thôi. Những điều “behind-the-scenes” như vậy không thể học được nếu chỉ nhìn vào code của người khác.
StackOverflow – The definitive C++ book guide and list
Trên Windows, Free Pascal là sự lựa chọn tốt cho Pascal. Ngoài ra còn có một số công cụ khác như Lazarus, Codeblocks, Delphi,... Đối C++ thì hồi trước mình hay dùng nhất là Dev-C++.
Tuy nhiên, mình khuyên là các bạn nên từ bỏ Windows và chuyển sang một hệ điều khác dựa trên nền tảng Unix như là Ubuntu. Hiện tại thì màn hình lập trình của mình trông giống như thế này:
Ở cửa sổ trái mình sử dụng vim, chỉ đơn thuần là một chương trình soạn thảo văn bản (text editor) của Ubuntu. Ở cửa sổ phải mình sử dụng terminal, nói nôm na là nơi bạn có thể điều khiển máy tính bằng các câu lệnh thay vì dùng chuột (giống như là MS-DOS thời xa xưa). Lưu ý là hai công cụ này đều có sẵn trong Ubuntu, bạn không cần phải cài đặt gì cả. Trong hình, mình có cài thêm một số Plugins cho vim để thêm màu mè, màn hình đen, hiển thị dòng cột (cảm ơn bạn mình là RR!). Mình cũng cài thêm theme Macbook cho máy nên màn hình trông giống MacOS mặc dù thật ra nó vẫn là Ubuntu.
Khi lập trình, mình viết code vào vim rồi sử dụng câu lệnh này để biên dịch (compile) chương trình.
g++ test.cpp -o a -O2 -Wall
Lựa chọn “-o a” sẽ biên dịch file test.cpp thành một file excutable tương tự như file .exe của Windows có tên là a trong cùng thư mục của file test.cpp. Vì thế, muốn chạy chương trình, mình chỉ cần chạy file a bằng câu lệnh sau:
./a
Nếu bạn không thích vim, có thể xài gedit hoặc là emacs hoặc bất cứ trình soạn thảo văn bản nào khác đều được. Bộ đôi (text editor + terminal) rất đa năng vì bạn có thể sử dụng nó cho nhiều ngôn ngữ khác nhau. Hơn nữa, trong một sản phẩm lập trình thật sự, bạn không phải compile một lúc một file nữa mà có thể là cả ngàn files. Lúc đó, bạn sẽ cần đến các công cụ chạy từ terminal. Cho nên, làm quen với terminal sớm là một lợi thế. Ngoài ra, trong tương lai gần, nếu thi tin học quốc tế (IOI), các bạn sẽ thi trên hệ điều hành Ubuntu.
Muốn học giỏi cần phải có thầy giỏi. Cho dù có tố chất đến mấy mà không biết cách khai phá thì cũng sẽ không thể đạt đỉnh cao. Nếu ai đó bảo bạn rằng chỉ có tự học mới giỏi được, thì người đó nói đúng. Nhưng nếu bạn chỉ ngồi trong giếng nhà tự mày mò tất cả thì theo mình đó không phải là tự học, mà chỉ là học một mình. Tự học bao gồm cả việc tự tìm thầy để mà học.
Khi mình đặt chân vào trường Phổ Thông Năng Khiếu, nơi có đội tuyển tin thuộc hàng top của đất nước thời bấy giờ, mình cứ ngỡ sẽ được dạy dỗ bởi những sư tổ tu luyện lâu năm, là những giáo sư tiến sĩ đầu tóc bạc phơ. Tuy nhiên, Năng Khiếu xây dựng cho mình một hình tượng khác về người thầy. Người “thầy” của mình ở Năng Khiếu ngoài thầy chủ nhiệm, còn là các anh khóa trên đi trước và các bạn trong cùng đội tuyển. Ngoài ra, mình cũng chủ động làm quen các anh học giỏi trong cả nước thời ấy như Khúc Anh Tuấn hoặc Phạm Quang Vũ để hỏi bài. Khi hỏi mình cũng thấy hơi ngại nhưng mà thông qua đó mình học được cách suy nghĩ rất hay của các anh. Có những bài tập đòi hỏi những kỹ thuật mà mình thật sự không thể nào biết được nếu chỉ ngồi học trong giếng nhà. Sau này, diễn đàn VNOI được các đàn anh lập ra cũng vì mục đích để mọi người tìm được những người thầy như vậy.
Tuy nhiên, mình khuyến cáo các bạn không nên quá lạm dụng các “thầy”, không nên coi các thầy như là một cỗ máy trả lời. Các “thầy” thường sẽ tận tình đỡ các bạn hơn nếu các bạn thể hiện được mình là người chịu khó tư duy và tìm tòi hơn là chỉ biết vòi vĩnh câu trả lời. Các bạn coi phim chắc cũng biết là cao nhân chỉ truyền bí kíp cho kẻ có tố chất. Một mẹo nhỏ là nhớ lịch sự nói “cảm ơn” sau mỗi lần hỏi.
Khi học Tin học bạn có một lợi thế đó là bài tập dường như là vô biên (SPOJ, Timus, POJ). Nhưng bạn không cần thiết làm hết tất cả chúng để giỏi. Vì thế ta thay vì hỏi tìm bài ở đâu, thì nên hỏi:
Mình gợi ý một số cách làm bài cơ bản như sau:
Làm hết những bài cơ bản: vào trang SPOJ hoặc VNOJ, mở list đề bài ra. Ở tiêu đề của cột “Users” khi bạn bấm vào đó một lần, thì các bài tập sẽ được sắp xếp theo thứ tự giảm dần số lượng người làm được. Bài ở những trang đầu tiêu là những bài cơ bản nhất, hầu như ai cũng làm được. Bạn nên làm khoảng vài trang đầu để luyện các kỹ thuật cơ bản.
Làm theo chủ đề: khi bạn đang làm những bài cơ bản sẽ gặp những bài bạn nghĩ mãi vẫn không ra. Không phải là bạn kém thông minh gì mà vì những thuật toán đó quá xa lạ với bạn. Vậy thì bạn nên tìm tòi làm những dạng bài tương tự để biết cách áp dụng các thuật toán mới học. Sau khi google mình tìm thấy trang này Problem classifier có lẽ khá hữu dụng.
Mình muốn dừng ở đây một chút để lưu ý là cách 1 và cách 2 phải nên được áp dụng bổ trợ cho nhau bởi vì một phương pháp chú trọng vào mở rộng kiến thức theo chiều rộng, còn phương pháp còn lại mở rộng theo chiều sâu. Chỉ làm theo kiểu gặp bài nào làm bài đó thì khó thể đúc kết được những kinh nghiệm sâu sắc. Ngược lại, làm theo chủ đề cho các bạn thời gian để tập trung suy nghĩ về một vấn đề, nhưng cẩn thận tránh đam mê quá một dạng bài mà trở nên không thoải mái khi làm những dạng bài khác.
Tham gia các kì thi trực tuyến: nếu hai phương pháp trên sẽ cho bạn nền tảng tốt thì phương pháp này sẽ đưa bạn đến đỉnh cao. Vì mục đích cuối của bạn là tham gia các kì thi quốc gia quốc tế, nên việc ngồi cả ngày trời để làm một bài tập trên các trang Online Judge một lúc nào đó sẽ trở thành một thói quen vô cùng nguy hại. Lúc đó, bạn nên chuyển qua làm bài theo thời gian thực. Có cảm nhận về áp lực thời gian, đầu óc bạn sẽ trở nên nhanh nhẹn hơn nhiều. Hơn nữa, khi thật sự thi đấu với những con người khác, bạn cũng sẽ có cảm xúc luyện tập hơn là chỉ ngồi cày Online Judge một mình. Hiện nay, các kì thi trực tuyến mọc lên như nấm (Codeforces, TopCoder, Hackerrank, USACO, COCI). Các bạn cũng có thể lấy bài từ các cuộc thi khác vào tạo ra cuộc thi của riêng rồi mời bạn bè tham gia (HUSTOJ).
Không có cách nào cả. Phải làm bài nhiều mới giỏi được. Nghiên cứu về các kiện tướng ở nhiều bộ môn cho thấy họ đều luyện tập ít nhất 10 ngàn giờ để đạt được trình độ đó.
Điều này thì có thể. Thậm chí mình cảm thấy làm quá nhiều sẽ dẫn đến hoặc là quá tải hoặc là sẽ trở một cỗ máy giải bài, giải bài xong thì bỏ qua ngay mà không suy nghĩ thêm gì nữa. Nên nhớ là thời gian luyện tập của các bạn có hạn, nên các bạn phải khai thác hết giá trị của từng bài tập bạn giải. Giải một bài tập xong rồi quăng sang một bên là vô cùng phí phạm. Việc các bạn giải được nhiều bài trước kì thi chẳng có nghĩa lý gì nếu bạn không rút ra được nhiều giá trị từ chúng.
Suy cho cùng, thì nguyên lý “muốn giỏi phải làm nhiều bài tập” vẫn không đổi. Tuy nhiên, thay vì làm các bài tập một cách tuần tự, các bạn có thể cùng một lúc giải nhiều bài tập bằng cách suy nghĩ về những biến thể của một bài tập vừa giải được. Hãy xem xem trong bài toán có những điều kiện nào thể thay đổi được, có thể tổng quát hóa lên được. Bài toán này có tính chất gì đặc biệt mà thuật toán của bạn lại giải được? Tính chất đó có thể xuất hiện dưới dạng khác hay không? Chỉ cần bỏ một ít thời gian đặt ra những câu hỏi dạng này và đào sâu khám phá, giá trị thu được khi giải một bài tập sẽ được nhân lên nhiều lần. Giải một bài mà như giải 10 bài là như vậy.
Một ví dụ đơn giản là trong lúc làm bài tập, mình nhận ra rằng thuật toán chia nhị phân được sử dụng để chuyên trị những bài tập đòi hỏi “cực tiểu hóa một giá trị cực đại” (hoặc ngược lại, ví dụ như là tìm tập hợp sao cho số lớn nhất trong tập nhỏ nhất trong tất cả các cách chọn). Trong một ví dụ khác, với bài QBDIVSEQ, nhận xét mấu chốt là “độ dài dãy con tăng dài nhất là số lượng ít nhất các dãy con giảm mà ta có thể phân chia dãy ban đầu thành”. Khi chứng minh nhận xét này, mình nhận thấy một tính chất đặc biệt của mối quan hệ “lớn bé” giúp cho chứng minh trở nên khả thi. Đó là tính chất bắc cầu: suy ra . Từ đó, mình suy nghĩ ra rằng thay vì sử dụng mối quan hệ “lớn bé” thông thường khi so sánh các số, ta có hoàn toàn có thể áp dụng nhận xét của bài này cho một mối quan hệ “lớn bé” khác (ví dụ quan hệ chia hết), miễn là tính chất bắc cầu được thỏa mãn.
Theo mình, có MỘT thứ tách biệt người học giỏi và người học không giỏi trong các kì thi Tin học quốc gia và quốc tế, đó là việc người học đó có thoải mái với tiếng Anh hay không. Việc thoải mái với tiếng Anh giúp cho bạn có thể vươn ra khỏi cộng đồng trong nước để tiếp cận với nhiều luồng tri thức của nhân loại. Hồi mình mới học Tin, tìm được một cuốn sách dạy thuật toán bằng tiếng Việt giống như là tìm cá mập trong vịnh Thái Lan vậy. Thực tế, chỉ có một cuốn sách về thuật toán được viết sát với nội dung thi quốc gia, do thầy Lê Minh Hoàng biên soạn. Nhưng chỉ luyện các thuật toán cơ bản trong sách thầy Hoàng thì không đủ để có thể cạnh tranh trên đấu trường quốc tế (em xin lỗi thầy ). Lúc ấy, muốn học thêm các kỹ thuật nâng cao về quy hoạch động hay cấu trúc dữ liệu, mình phải tìm đến trang dạy thuật toán của Topcoder hoặc đọc lời giải bằng tiếng Anh của các kì thi quốc tế. Hiện nay, các thành viên của Codeforces có viết nhiều trang blog mô tả đủ loại kỹ xảo lập trình tiên tiến nhất. Đó là một mỏ vàng cần được khai thác.
Thoái mái với tiếng Anh cũng giúp bạn hòa nhập với cộng đồng luyện thi lập trình trên thế giới thông qua việc tham gia các kì thi trực tuyến (đề tiếng Anh). Bạn từ một con cá nhỏ trong cái ao làng, vươn ra biển lớn. Bạn sẽ thấy rằng con cá lớn nhất trong cái ao của bạn chẳng to bằng một phần trăm con cá lớn của đại dương. Khi được tôi luyện với con những con cá đó, dù cố tình hay vô ý, khả năng sinh tồn của bạn rồi cũng sẽ được nâng cao.
Bài viết đến đây là hết, chúc các bạn tìm được nhiều niềm vui với môn Tin học