HƯỚNG DẪN THỰC HÀNH MÔN CƠ SỞ LẬP TRÌNH 2

Bài 1: Hãy xây dựng các phương pháp phát sinh tập các phương án tiềm năng ở dạng : a. Tập con của một tập hợp gồm N phần tử. b. Tổ hợp K phần tử từ tập hợp N phần tử. c. Chỉnh hợp K phần tử từ tập hợp N phần tử. d. Hoán vị của tập hợp N phần tử. Bài toán 1: Liệt kê tổ hợp k phần tử từ tập hợp có n phần tử Liệt kê các tập con của tổ hợp chập 3 của 5 (n=5, k=3) với S = {0, 1, 2, 3, 4} 5! 3 Ta có C5 = = 10 3!(5 − 3)! Tổ hợp chập 3 của 5 có 10 tập con, được liệt kê như sau: 1. {0, 1, 2} 2. {0, 1, 3} 3. {0, 1, 4} 4. {0, 2, 3} 5. {0, 2, 4} 6. {0, 3, 4} 7. {1, 2, 3} 8. {1, 2, 4} 9. {1, 3, 4} 10. {2, 3, 4} - Như vậy (trong trường hợp n=5, k=3): + Tập con đầu tiên có dạng: + Tập con cuối cùng có dạng: - Trường hợp tổng quát: + Tập con đầu tiên có dạng: + Tập con cuối cùng có dạng: {0, 1, 2} {2, 3, 4} {0, 1, …, k-1} {n-k, n-k+1, …, n-1}

- Nhận xét: Ta sẽ in ra tập con bằng cách in ra lần lượt các phần tử của nó theo thứ tự tăng dần. Từ đó ta có nhận xét nếu x = {x0, x1, …, xk-1} và x0 < x1 < … < xk-1 thì giới hạn trên (giá trị lớn nhất có thể nhận) của xk-1 là n-1, của xk-2 là n-2, … - Cụ thể: + Giới hạn trên của xi = n – k + i + Giới hạn dưới của xi = xi-1 + 1 Như vậy nếu ta đang có một dãy x đại diện cho một tập con, nếu x là cấu hình kết thúc có nghĩa là tất cả các phần tử trong x đều đạt tới giới hạn trên thì quá trình sinh kết thúc, nếu không thì ta phải sinh một dãy x mới tăng dần thoả mãn vừa đủ lớn hơn dãy cũ theo nghĩa không có một tập con k phần tử nào chen giữa chúng khi sắp thứ tự từ điển. Ví dụ: n = 9, k = 6. Cấu hình đang có x = {0, 1, 5, 6, 7, 8}. Các phần tử x2 đến x5 (tính từ x0 đến xn1) đã đạt tới giới hạn trên, do đó ta không thể tăng các giá trị từ x2 đến x5 lên được, ta phải tăng x1=1 lên 1 đơn vị x1=2. Được cấu hình mới là x = {0, 2, 5, 6, 7, 8}, cấu hình này đã thoả mãn lớn hơn cấu hình trước, nhưng chưa thoả mãn tính chất vừa đủ lớn. Muốn vậy ta phải thay x2 đến x5 bằng các giới hạn dưới của nó. Tức là: Hoàng Đăng Quang – Khoa CNTT – HUFLIT

x2 = x1 + 1 = 3 x3 = x2 + 1 = 4 x4 = x3 + 1 = 5 x5 = x4 + 1 = 6 Ta được cấu hình mới x = {0, 2, 3, 4, 5, 6} là cấu hình kế tiếp. Nếu muốn tìm tiếp, ta lại nhận thấy rằng x5 = 6, chưa đạt giới hạn trên, như vậy chỉ cần tăng x5 lên 1 đơn vị, vậy cấu hình tiếp theo là: x = {0, 2, 3, 4, 5, 7}. Vậy kỹ thuật sinh tập con kế tiếp từ tập x được xây dựng như sau: - Bắt đầu tìm từ cuối dãy lên đầu dãy cho tới khi gặp một phần tử xi chưa đạt giới hạn trên (xi < n –k + i). - Nếu tìm thấy phần tử xi thoả điều kiện trên thì tăng xi đó lên 1. Và đặt tất cả các phần tử phía sau xi bằng giới hạn dưới (xi+1 = xi + 1). - Nếu không tìm thấy (hay phần tử x0 đã đạt giới hạn trên) thì dừng thuật toán. Hướng dẫn cách viết chương trình: * Cách 1: Dùng vòng lặp 1. void Combitation(int k, int n): Hàm liệt kê các tổ hợp chập k của tập hợp gồm n phần tử. - Cấp phát mảng lưu tổ hợp (k phần tử). - Khởi tạo các phần tử: xi = i (i = 0…n-1) - Xuất cấu hình đầu tiên. (cấu hình khởi tạo) - Trong khi còn có thể sinh các tập con tiếp theo o Phát sinh cấu hình tiếp theo. o Xuất cấu hình vừa mới sinh được. - Huỷ vùng nhớ đã cấp phát (cho mảng lưu tổ hợp). 2. void Next(int x[], int k, int n): Hàm sinh tập con tiếp theo từ tập con x cho trước theo kỹ thuật phát sinh tập con đã trình bày ở trên. 3. void Print(int x[], int k): Hàm xuất k phần tử của tổ hợp hiện tại ra màn hình. * Cách 2: Dùng đệ quy Trong cách này, chúng ta sẽ không phát sinh cấu hình mới từ cấu hình hiện tại, mà sẽ phát sinh cấu hình mới bằng cách phát sinh từng phần tử (từ x[0] đến x[n-1]) cho từng cấu hình một. Bắt đầu từ phần tử i=0, hàm Next() sẽ gán lần lược các giá trị mà x[0] có thể nhận được (các giá trị từ giới hạn dưới đến giới hạn trên), tại đây hàm Next() sẽ gọi đệ quy để tiếp tục phát sinh phần tử x[1] của cấu hình, …. tiếp tục như vậy, khi đã phát sinh đủ n phần tử cho cấu hình (i=n-1), xem như đã tạo được một cấu hình mới. 1. void Combitation(int k, int n): Liệt kê các tổ hợp chập k của tập hợp n phần tử. - Cấp phát mảng lưu tổ hợp (k phần tử). - Gọi hàm Next(x, k, n, 0). - Huỷ vùng nhớ đã cấp phát. 2. void Next(int x[], int k, int n, int i): Hàm đệ quy, phát sinh tập con cho tổ hợp, với biến i là vị trí phần tử thứ i của tập con được phát sinh. - Tìm xmin = giới hạn dưới của phần tử xi - Tìm xmax = giới hạn trên của phần tử xi Hoàng Đăng Quang – Khoa CNTT – HUFLIT

3} 14. 2} 9. Bài toán 2: Liệt kê tập con của tập hợp có n phần tử Ứng dụng bài toán tìm tổ hợp chập k của tập hợp n phần tử ở trên để tìm như sau: void Subset(int n) { for (int i=0. 2} {0. 1. i < n. 1. 1. {3. 0} 11. k. 1. 0. i+1) } 3. 1. 5. ta sẽ tìm cách sinh ra các hoán vị tiếp theo từ cấu hình ban đầu. 1. 0. 1. 2. 3} {3. {3. 3. 1. {3. {3. 3} 16. {2. 2. {1. 2. 1. 3. Nếu i = k-1 (tập con đã đủ k phần tử) Xuất cấu hình hiện tại ra màn hình (hay ra file). và trong mỗi hoán vị mới sinh được phát sinh vẫn phải đảm bảo hoán vị sau phải vừa đủ lớn hơn so với hoán vị trước (so sánh theo thứ tự từ điển). 3}. void Print(int x[]. n). …. 3. 0. 1} 7. 1} 18. 2} {0. 1} 24. 2. 0. i++) { Combitation(i+1. {0. 1} {0. 0. 3. 1} 15. 0. n-1} {n-1. 0. 2. 2} 12. 1. n. 3. 2. 1. 2. {2. 1. 0. {2. {2. 3. {1. 2} 22. 3. 2. 3} 10. 0} 13. 3. …. 0. int k): Xuất k phẩn tử của tổ hợp hiện tại màn hình (hay ra file). 3. 0. 1} 21. 3. 2. {3. 3. {1. 2. 2. {2. Hoàng Đăng Quang – Khoa CNTT – HUFLIT . 0. 2. 3. 0} 17. n = 4. 2. 0. {1. {2.- for(j: xmin xmax) { a[i] = j. 6. 1. Số hoán vị của S = Pn = n! = 4! = 24. tiếp tục sinh như vậy cho đến khi đạt đến được cấu hình hoán vị cuối cùng. {3. 0} 23. 1. {1. 2. } } Bài toán 3: Liệt kê hoán vị của tập hợp có n phần tử Liệt kê các hoán vị của S = {0. 0} {0. 1. 0} Như vậy trong ví dụ ở trên: + Hoán vị đầu tiên sẽ là: + Hoán vị cuối cùng là: Tổng quát: + Hoán vị đầu tiên là: + Hoán vị cuối cùng là: Cũng như trong bài toán tổ hợp ở trên. 4. 2. 3. 0} {0. {1. 2} 20. 0} 19. 3} {0. 2. 3} 8. 3} {0. Ngược lại: (Gọi đệ quy để tiếp tục xác định phần tử thứ i+1 cho tập con) Next(x. 1. 1. nghĩa là không thể có một hoán vị nào khác có thể chen giữa hoán vị hiện tại và hoán vị vừa mới sinh ra được. n-2.

điều đó có nghĩa là ta không thể tiếp tục hoán vị 4 phần tử này để tìm ra phần tử nào khác lơn hơn hoán vị hiện tại được. 2. 4.Xuất cấu hình đầu tiên. do x[0] đã bằng 2 rồi (phần tử sau không được chọn những giá trị mà ở phần tử trước đã chọn). Ta có thể xem hoán vị {1. Hoàng Đăng Quang – Khoa CNTT – HUFLIT . do chọn như vậy hoán vị mới sẽ nhỏ hơn hiện tại. 5}. ta thấy chúng được sắp xếp giảm dần. Trong trường hợp hoán vị hiện tại là {1. 5. nên ta sẽ đi ngược từ vị trí cuối dãy n-1 ngược lên đến vị trí i+1. Khi đó muốn biểu diễn nhỏ nhất cho các giá trị trong đoạn cuối thì ta chỉ cần đảo ngược (lấy đối xứng gương) cho đoạn cuối (đoạn từ x[2] đến x[5]). ta cũng không thể chọn 2 được. 3.Cấp phát mảng lưu hoán vị (n phần tử). 3} thì hoán vị kế tiếp sẽ là {1. nếu x[i] > x[i+1] thì ta sẽ giảm giá trị i xuống 1 đơn vị để tiếp tục xét phần tử x[i-1] và x[i]… cho đến khi i=-1 (toàn bộ các phần tử trong dãy đã sắp xếp giản dần. 1. + Hoán vị x[k] và x[i]. . Vì cần một hoán vị vừa đủ lớn hơn hiện tại. Nếu đổi chỗ x[4] cho x[1] thì ta sẽ được x[1] = 3 và đoạn cuối cùng vẫn được sắp giảm dần. 0. x[5]. Vậy chỉ còn lại các giá trị 5. 0}. (cấu hình khởi tạo) . Còn các giá trị (x[2]. 2. xét 4 phần tử cuối cùng. 0. . 4. . do đó ta sẽ chọn x[1] = x[4] = 3. x[3]. 3} có đoạn cuối giảm dần chỉ gồm một phần tử là {3}. Hướng dẫn cách viết chương trình: * Cách 1: Dùng vòng lặp 1. 0}. Do đó ta phải xét đến x[1] = 1. 3. 5. x[4]. 4. Cũng vì tính vừa đủ lớn nên ta sẽ tìm một biểu diễn nhỏ nhất của 4 số này để gán cho x[2]. Vậy ta phải chọn giá trị nào ? Ta không thể chọn giá trị 0 được.Nếu i = -1: Dừng thuật toán (Hoán vị đã đạt đến cấu hình cuối cùng). o Xuất cấu hình vừa mới sinh được.Trong khi còn có thể sinh các tập con tiếp theo o Phát sinh cấu hình tiếp theo.Huỷ vùng nhớ đã cấp phát (cho mảng lưu hoán vị).Nếu 0 ≤ i ≤ n-2: + Trong đoạn cuối giảm dần tìm phần tử x[k] đầu tiên thoả điều kiện x[k] > x[i] (do dãy cuối là dãy giảm. Vậy hoán vị mới sẽ là {2. 1.Giả sử ta có hoán vị hiện tại là x = {2.Khởi tạo các phần tử: xi = i (i = 0…n-1) . hay cụ thể hơn là tìm chỉ số i của phần tử x[i] đứng liền trước của đoạn cuối giảm dần (đoạn cuối giảm dần sẽ đi từ vị trí i+1 đến n-1). x[4].Xác định đoạn cuối giảm dần dài nhất. 3. 0. x[5]) sẽ lấy trong tập {1. 4. và tìm một giá trị khác để thay thế. . Vậy kỹ thuật sinh hoán vị kế tiếp từ hoán vị hiện tại có thể xây dựng như sau: . hoán vị đã đạt cấu hình cuối cùng) hay không còn thoả điều kiện x[i] > x[i+1] thì dừng lại. 3}. Ta có nhận xét gì qua ví dụ này ? Đoạn cuối của hoán vị hiện tại được xếp giảm dần. nếu tìm thấy vị trí k đầu tiên thoả x[k] > x[i] thì dừng lại). . Như vậy ta sẽ bắt đầu từ vị trí của phần tử kế cuối (vị trí n-2) của cấu hình hiện tại. void Permutation(int n): Hàm liệt kê các hoán vị tập hợp gồm n phần tử. x[4] = 3 là số nhỏ nhất trong đoạn cuối giảm dần thoả mãn điều kiện lớn hơn x[1]=1. 4. + Lật ngược đoạn cuối giảm dần (lấy đối xứng gương) để đoạn này (từ vị trí i+1 đến n – 1) trở thành đoạn tăng dần. 0. x[3].

check. Hoàng Đăng Quang – Khoa CNTT – HUFLIT . 3} {1. chúng ta sẽ không phát sinh cấu hình mới từ cấu hình hiện tại. void Print(int x[]. 2} {3. 3} {2. 1. int check[]. các phần tử còn lại được sinh từ bài toán hoán vị. 1} {0. check. int n. 1. 1. int n): Hàm xuất n phần tử của hoán vị ra màn hình. 2} {2. Hàm trả về 1 nếu sinh được hoán vị mới. n. 3. Bắt đầu từ phần tử i=0. tại đây hàm Next() sẽ gọi đệ quy để tiếp tục phát sinh phần tử a[1] của cấu hình. ngược lại trả về 0. . . trong cách dùng đệ quy này. 2} {1. 1. 2. Bài toán 4: Liệt kê chỉnh hợp k phần tử từ tập hợp có n phần tử Kết hợp bài toán tổ hợp và bài toán hoán vị để liệt kê chỉnh hợp. 3} {0. 0. 3} {0. void Permutation(int n): Hàm liệt kê các hoán vị tập hợp gồm n phần tử. phát sinh hoán vị. void Print(int x[]. 2.Cấp phát mảng lưu hoán vị (mảng a) và mảng dùng để đánh dấu các giá trị (từ 0 đến n-1) của hoán đã được chọn hay chưa (mảng check). 2} {2. 2.Next(x. 3. 0} {2. mà sẽ phát sinh cấu hình mới bằng cách phát sinh từng phần tử (từ x[0] đến x[n-1]) cho từng cấu hình một.Duyệt qua tất cả các phần tử trong mảng check { Nếu tìm thấy giá trị j nào đó chưa được chọn { Gán x[i] = j Nếu hoán vị đã đủ n phần tử (i = n-1): Xuất hoán vị vừa phát sinh được. 1. n.3. các phần tử được gạch dưới là các phần tử được sinh ra từ bài toán tổ hợp. 2. 1} {3. 2. 0. Ngược lại { Đánh dấu giá trị j đã được chọn Next(x. tiếp tục như vậy. 3 Ví dụ: Liệt kê chỉnh hợp 3 phần tử từ tập hợp có 4 phần tử ( A4 = 4. 0. 0} {3. void Next(int x[]. 3. 0} {2.2. hàm Next() sẽ gán lần lược các giá trị mà x[0] có thể nhận được (các giá trị đưa được đánh dấu đã sử dụng trong mảng check). 1} Trong phần liệt kê trên. int n): Xuất hoán vị ra màn hình (hay ra file). i+1) Bỏ đánh dấu đỉnh j (đệ quy quay lui). int n): Hàm sinh hoán vị tiếp theo từ hoán vị hiện tại (theo kỹ thuật phát sinh hoán vị đã trình bày ở trên). 1} {3. 0. 0. 0} {1. . 2. 3. 3. 0.2 = 24 ) {0. với biến i là vị trí phần tử thứ i của hoán vị cần phát sinh. . 0} {1. 1} {0. 2} {1. 3. 3} {2. int Next(int x[]. } } } 3. 0} {3. xem như đã tạo được một hoán vị mới. 2. 0). khi đã đủ n phần tử cho hoán vị (i=n-1). …. 1} {3. int i): Hàm đệ quy. 1.Huỷ 2 mảng đã cấp phát. 1. 3. 2} {0. * Cách 2: Dùng đệ quy Cũng như trong bài toán tổ hợp. 3} {1.

Khởi tạo: x[i] = i (i = 0.Gọi hàm hoán vị để sinh ra tất cả các hoán vị của tổ hợp đầu tiên: Permutation(a. Yêu cầu: Cài đặt bài toán này dùng 2 cách. Nếu không tìm thấy phần tử x[i] nào bằng 0 (lúc này i = -1). 0 1 1 0 8. vòng lặp và đệ quy. nếu gặp phần tử x[i] = 0 thì gán x[i] = 1.Huỷ mảng lưu chỉnh hợp. 0 0 1 1 9. dừng thuật toán. 15. int n) { . int n): Hàm sinh dãy nhị phân tiếp theo. Dãy nhị phân đã đạt cấu hình cuối cùng.Trong khi còn có thể phát sinh tiếp các tổ hợp tiếp theo: Phát sinh tổ hợp tiếp theo: NextCombitation(a. k). xem như đã phát sinh xong dãy nhị phân tiếp theo. 16. 1000 1001 1010 1011 13. . nếu phần tử x[i] = 1 thì gán x[i] = 0. 0 0 0 1 3. 0 0 0 0 5.void Accordance(int k. 0 1 1 1 4. 14. không thể phát sinh tiếp dãy nhị phân tiếp theo. } * Bài tập tương tự: Dùng phương pháp sinh để sinh dãy nhị phân có độ dài n Liệt kê dãy nhị phân có chiều dài là 4: (có 24 = 16) 1.. 0 1 0 1 2.k-1) . cài đặt 3 hàm sau: void Binary(int n): Hàm liệt kê các dãy nhị phân có độ dài n int Next(int x[]. 0 1 0 0 6. 12. 0 0 1 0 7. 10. int n): Hàm in dãy nhị phân có độ dài n. 11. . Hướng dẫn cách viết chương trình: Tương tự như ở bài toán liệt kê tổ hợp ở trên. k). Hoàng Đăng Quang – Khoa CNTT – HUFLIT .Cấp phát mảng lưu chỉnh hợp (k phần tử) . k. ngược lại trả về 0. nếu sinh thành công trả về 1. 1100 1101 1110 1111 Kỹ thuật sinh dãy nhị phân có độ dài n phần tử như sau: Bắt đầu từ cuối dãy. void Print(int x[]. Gọi hàm hoán vị để liệt kê tổ hợp vừa mới được phát sinh này: Permutation(a. n).

Point points[MaxN]. . c. * Viết các hàm sau: . Hãy thực hiện các yêu cầu sau đây: a. //Số điểm tối đa chương trình có thể xử lý //Cấu trúc lưu toạ độ của một điểm //Số điểm cần xét (đọc từ tập tin dữ liệu) //Mảng chứa các điểm cần xét. Áp dụng bài toán liệt kê tổ hợp ở bài 1 và định lý Pitago để giải câu này. int n.) ta có thể viết điều kiện đơn giản sau: if (a == b). tuỳ thuộc và độ sai số cho phép. double b) { return fabs(a . Point C). B: double Distance(Point A. Next(). } Hướng dẫn cách viết chương trình: * Cấu trúc dữ liệu: #define MaxN 100 struct Point { double x. Liệt kê tất cả các tam giác vuông tạo thành từ tập điểm trên. Chú ý cách so sánh 2 số thực có bằng nhau hay không. Liệt kê tất cả các tứ giác lồi tạo thành từ tập điểm trên. double b). ta sẽ xuất toạ độ của 3 điểm này ra màn hình (hay ra tập tin văn bản)... Nếu đúng là tam giác vuông.Hàm so sánh 2 số thực có bằng nhau hay không: int Equal(double a.00001. Point B. b. int Equal(double a.b) <= epsilon.Hàm đọc dữ liệu từ tập tin văn bản chứa danh sách các đỉnh: void ReadData(). .Hàm tính khoảng cách giữa 2 điểm A. Riêng hàm Print thay vì là hàm dùng để in tổ hợp ta sẽ gọi hàm IsRightTriangle() để kiểm tra xem 3 điểm được tạo từ cấu hình hiện tại của tổ hợp có là tam giác vuông không. với a. b là 2 biến kiểu int. . Print(). Câu b: Liệt kê tất cả các tứ giác lồi tạo thành từ tập điểm cho trước. Hàm kiểm tra 2 số thực có bằng nhau hay không ? const double epsilon = 0. . Nhưng đối với số thực. Point B). }. Để kiểm tra 2 biến chứa giá trị nguyên (kiểu int.Hàm kiểm tra 3 điểm có hợp thành tam giác vuông hay không: int IsRightTriangle(Point A. y.Bài 2: Cho tập hợp gồm N điểm trên mặt phẳng. Xây dựng bao lồi tạo thành từ tập điểm trên. long. Áp dụng bài toán liệt kê tổ hợp ở bài 1 và bài toán kiểm tra một tứ giác có phài là tứ giác lồi hay không. Hoàng Đăng Quang – Khoa CNTT – HUFLIT . Câu a: Liệt kê tất cả các tam giác vuông tạo thành từ tập điểm.Do bài toán này chính là bài tìm tổ hợp chập 3 trong n phần tử nên ta sẽ viết thêm các hàm của bài toán tổ hợp như: Combitation(). nghĩa là 2 biến giá trị thực không nhất thiết phải hoàn toàn giống nhau mới được xem là bằng nhau.

x*A.Xét cạnh AD: B. }.Xét cạnh CD: A.StraightLine Equation(Point A. D. * Viết các hàm sau: . C không nằm cùng phía so với cạnh AD Hướng dẫn cách viết chương trình: * Khai báo cấu trúc dữ liệu: tứ giác 2 không là tứ giác lồi.Bài toán kiểm tra một đa giác n đỉnh có phải là đa giác lồi hay không ? Đa giác là lồi nếu với mọi cạnh của đa giác.y. ta chỉ cần thế toạ độ của 2 điểm C và D vào đường thẳng (AB).x – A. ví dụ ta có 4 điểm A. //Phương trình tổng quát của đường thẳng qua 2 điểm có dạng: ax + by + c = 0 struct StraightLine { double a.y – B. y. c = A. B không nằm cùng phía so với cạnh CD . để kiểm tra xem điểm C và D có nằm cùng phía với đường thẳng đi qua 2 điểm A. nếu 2 đỉnh nối với cạnh này đều nằm cùng một phía (nghĩa là khi thế toạ độ của 2 đỉnh này vào phương trình tổng quát của cạnh này sẽ có kết quả cùng dấu với nhau). với: a = A.y – B.y. }. .x*B. B.x. //Điểm (x. C. Ta có phương trình tổng quát của đường thẳng qua 2 điểm có dạng: ax + by + c = 0. c. . Hoàng Đăng Quang – Khoa CNTT – HUFLIT . ta phải giải quyết thêm bài toán kiểm tra 2 điểm có cùng nằm ở 1 phía của đường thằng đi qua 2 điểm không. b = B. Point B): Hàm viết phương trình tổng quát của đường thẳng qua 2 điểm A. C nằm cùng phía so với cạnh BC. Đề giải quyết bài toán này. B trả về struct StraightLine là phương trình đường thẳng qua 2 điểm A.Xét cạnh BC: A. b. A B D C Tứ giác 1 (Tứ giác lồi) C Tứ giác 2 (Tứ giác lõm) A D B Ở tứ giác 2: . tứ giác 2 không là tứ giác lồi. Trong bài toán kiểm tra đa giác lồi ở trên. D nằm cùng phía so với cạnh AB. y) struct Point { double x. nếu kết quả thế điểm của điểm C và điểm D vào đường thẳng (AB) đều cùng dấu với nhau thì C và D nằm cùng phía so với đường thẳng (AB).Xét cạnh AB: C. B hay không. B.

. B. Point P): Hàm tính giá trị khi thế toạ độ của điểm P vào phương trình tổng quát của đường thẳng D. .int IsSameSide(Point A. gồm n điểm (n ≥ 3) trong mặt phẳng 2 chiều (có thể mở rộng ra ở mặt phẳng với số chiều lớn hơn. đây là bài toán tìm tổ hợp chập 4 của n phần tử kết hợp với bài toán kiểm tra 4 điểm có tạo thành một đa giác lồi hay không. p10. B không. trong bài tập này chỉ xét bao lồi ở mặt phẳng 2 chiều).Nếu tập P hữu hạn.int IsConvexPolygon(Point P[]. Cũng tương tự câu a. Ví dụ: Cho tập P = {p1. p2. p2. p8. nếu tích của chúng ≥ 0. Point B. p7} (Chú ý: các đỉnh trong tập S được liệt kê theo thứ tự ngược chiều kim đồng hồ) Một số thuật toán tìm bao lồi Thuật toán Package Wrap Graham Scan Quickhull Mergehull Sweep Line Quick Elimination Độ phức tạp NM N log M N log N N log N N log N N Với: N là số điểm của tập P M là số điểm của tập S Hoàng Đăng Quang – Khoa CNTT – HUFLIT . p9. + Thế toạ độ điểm C và D vào đường thẳng (AB). ngược lại trả về kết quả 0. Point C. . trả về kết quả là 1 (cùng phía). . Point D): Hàm kiểm tra điểm C và D có nằm cùng phía so với đường thẳng qua 2 điểm A. + Viết phương trình đi qua 2 điểm A. int n): Hàm kiểm tra đa giác có phải là đa giác lồi hay không.Cho tập P.Tìm tập các điểm S nhỏ nhất chứa toàn bộ các điểm của tập P..double CalculateValue(StraightLine D. Câu c: Xây dựng bao lồi tạo thành từ tập điểm trên Bài toán bao lồi (convex hull) . tập S là tập các đỉnh của đa giác lồi duy nhất chứa toàn bộ các điểm của P. … p10} p5 p9 p2 p8 p6 p3 p1 p4 p7 p10 0 Kết quả: S = {p5.

Dời trục toạ độ đến p0 (p0 làm gốc toạ độ).cos θ y = r. Sắp xếp các điểm của tập P tăng dần theo góc tạo bởi p0 và điểm đó với trục X (sau khi sắp xong tập P sẽ được sắp lại theo thứ tự này). 2. Để sắp xếp các điểm theo thứ tự tăng dần theo góc. ta xem hệ trục mới này là hệ trục toạ độ ở hệ toạ độ cực (Polar Coordinates). Tính góc θ cho mỗi điểm và sắp tập P lại theo thứ tự tăng dần theo góc θ.Thuật toán Graham Scan Bước 1: Khởi tạo. 3.sin θ r = x2 + y 2 θ = tan −1    y x Hoàng Đăng Quang – Khoa CNTT – HUFLIT . x = r. Tìm điểm p0 thuộc P có toạ độ y nhỏ nhất (p0 sẽ là một điểm của tập S). sắp xếp 1.

pi) >= 0) stack. Dữ liệu kết quả được ghi ra tập tin BAOLOI.x)*(p3.(p3. } S = các phần tử còn lại sau cùng trong stack.length >= 2 && CrossProduct(stack.Bước 2: Xác định bao lồi Function ConvexHull(P) { Khởi tạo stack rỗng. p2. stack.x .p1.y .p1. p3 theo thứ tự thuận chiều kim đồng hồ. } Hàm CrossProduct() là hàm xác định 3 điểm p1. . p3) { Return (p2.Push(p2). p3 thẳng hàng. là số điểm của tập S.top. Hoàng Đăng Quang – Khoa CNTT – HUFLIT . p2. p3 có được xếp theo thứ tự thuận/ngược chiều kim đồng hồ hay không (thực chất là hàm tính diện tích hình tam giác. .y) .Push(pi). p2.m dòng tiếp theo.Dòng đầu tiên là số nguyên n (4 ≤ n ≤ 1000). stack.x .p1. mỗi dòng gồm 2 số nguyên là toạ độ (x. stack.p1. y) của điểm pi ∈ P.OUT: . y) của điểm pi ∈ S. Nếu diện tích < 0 thì các điểm p1. p2 p3 >0 p3 p1 p2 <0 p1 Yêu cầu: Viết chương trình xác định bao lồi từ tập điểm cho trước Dữ liệu được dọc thừ tập tin BAOLOI.n dòng tiếp theo. stack.x)*(p2.y). mỗi dòng gồm 2 số nguyên là toạ độ (x. p2.Push(p1).second. for (i = 3 n-1) { while (stack. Return S.Pop(). } Function CrossProduct(p1.Push(p0).y .Dòng đầu tiên là số nguyên m. stack. Nếu diện tích = 0 thì các điểm p1. là số điểm của tập P. đúng ra phải chia toàn bộ kết quả cho 2 và lấy giá trị tuyệt đối.INP: . p3 theo thứ tự ngược chiều kim đồng hồ. nhưng trong trường hợp này chỉ quan tâm đến dấu trả về từ hàm này nên không cần chia 2 và lấy giá trị tuyệt đối) Nếu diện tích > 0 thì các điểm p1. p2.

Khởi tạo biểu thức F rỗng. Duyệt từ trái sang phải các thành phần của biểu thức E. 2. c. Nếu y là dấu ngoặc mở thì lấy ra khỏi ngăn xếp 3.OUT 3-15-8+26 = 6 Ví dụ: (câu b) KHOIPHUC. Nếu x là toán hạng.INP 78 2377426 KHOIPHUC. Lặp lại bước 2 cho đến hết biểu thức E 4. nối x vào bên phải biểu thức kết quả F. d.Nếu không thể khôi phục lại được các toán tử cho biểu thức. Đọc phần tử y ở đầu ngăn xếp b.Lặp lại bước a c. Nếu ngăn xếp rỗng thì đưa x vào ngăn xếp. b.Lấy y ra khỏi ngăn xếp .Bài 3: Khôi phục các toán tử cho biểu thức Cho biểu thức X = x1 ? x2 ? x3 ? … ? xn. a. Lần lược lấy các phần tử con lại của ngăn xếp nối vào bên phải biểu thức F cho đến khi ngăn xếp rỗng. gồm 2 dòng: . Nếu x là dấu ngoặc mở thì đưa vào ngăn xếp.OUT. xuất giá trị 0. Nếu x là dấu ngoặc đóng thì: a. Nếu độ ưu tiên của y cao hơn hay bằng x thì: .Nối y vào bên phải biểu thức kết quả F. .OUT 2*3/7*7+4*2-6 = 8 * Hướng dẫn cách tính giá trị biểu thức ở câu b Bước 1: Dùng ký pháp nghịch đảo Ba Lan chuyển biểu thức từ trung tố (infix) sang hậu tố (posfix) .Dữ liệu đầu vào là biểu thức E ở dạng trung tố . Toán tử: +.INP. 2. Toán tử: +. với mỗi thành phần x thực hiện các bước sau: 2.3. Nếu độ ưu tiên của x cao hơn của y thì đưa x vào ngăn xếp.Ngược lại xuất biểu thức sau khi được khôi phục các toán tử Ví dụ: (câu a) KHOIPHUC. Đọc phần tử y ở đầu ngăn xếp. -. Với xi là các toán hạng và ‘?’ là các toán tử của biểu thức. theo định dạng sau: . 2. Nếu y là phép toán thì . Hoàng Đăng Quang – Khoa CNTT – HUFLIT .Nối y vào bên phải biểu thức F . .2. *. Yêu cầu: Viết chương xác định các toán tử để thay thế cho các dấu ‘?’.Dòng thứ 2 gồm n số nguyên. / Dữ liệu đọc vào từ tập tin KHOIPHUC.Lấy y ra khỏi ngăn xếp.INP 46 3 15 8 26 KHOIPHUC. mỗi số nguyên cách nhau một khoảng trắng Dữ liệu kết quả được ghi ra ở tập tin KHOIPHUC.Dòng đầu tiên gồm số nguyên n và số thực X (2 <= n <= 20) . Nếu x là toán tử thì a.Lặp lại bước a.Dữ liệu đầu ra là biểu thức F ở dạng hậu tố Thuật toán chuyển biểu thức từ trung tố ra hậu tố 1. . 2.4.1. b.

2.1. Jan Łukasiewicz là một nhà toán học người Ba Lan. Duyệt lần lược các phần tử của biểu thức F. Ông sinh ra ở Lwów. Nếu x là toán hạng thì đưa vào ngăn xếp. với thành phần x. Giá trị duy nhất còn lại trong ngăn xếp là kết quả của biểu thức E. Ukraina). Galicia (nay là Lviv. Lặp lại bước 1 cho đến khi hết biểu thức 3.Ví dụ: Chuyển từ trung tố ra hậu tố biểu thức E = 2*3/7*7+4*2-6 Thành phần của biểu thức E 2 * 3 / 7 * 7 + 4 * 2 6 Ngăn xếp (stack) * * / / * * + + +* +* ∅ Biểu thức kết quả F (ở dạng hậu tố) 2 2 23 23* 23*7 23*7/ 23*7/7 23*7/7* 23*7/7*4 23*7/7*4 23*7/7*42 23*7/7*42*+ 23*7/7*42*+6 23*7/7*42*+6Kí pháp Ba Lan do nhà logic toán Jan Łukasiewicz đề xuất khoảng năm 1920. Ví dụ: Tính giá trị biểu thức được biểu diễn ở dạng hậu tố F = 2 3 * 7 / 7 * 4 2 * + 6 Thành phần của biểu thức F 2 3 * 7 / 7 * 4 2 * + 6 Ngăn xếp (stack) 2 23 6 67 (6/7) (6/7) 7 6 64 642 68 14 14 6 8 Hoàng Đăng Quang – Khoa CNTT – HUFLIT . 1. thực hiện: 1. 2. Bước 2: Tính giá trị biểu thức Thuật toán tính giá trị biểu thức ở dạng hậu tố 1. Nếu x là toán tử thì lấy 2 phần tử đầu ngăn xếp thực hiện phép tính theo toán hạng và đưa kết quả tính được trở lại ngăn xếp. Lĩnh vực nghiên cứu chính của ông là logic toán.

Max Bezzel.W. là số cách huấn luyện viên có thể chọn. trong đó quân mã sau khi đi hết tất cả 64 ô của bàn cờ (kể cả ô xuất phát). cùng cột hay trên 2 đường chéo đi ngang qua vị trí quân hậu. Yêu cầu: Viết chương trình đọc từ tập tin văn bản danh sách tên các cầu thủ. . và J. Các lời giải đầu tiên được đưa ra bởi Franz Nauck năm 1850. Có những hành trình. Huấn luyện viên của mỗi đội cần trình trọng tài một danh sách sắp thứ tự k cầu chủ trong số n cầu thủ của đội để tham gia đá.064 lời giải trong đó quân mã có thể kết thúc tại chính ô mà nó khởi đầu. thì từ ô cuối của hành trình không thể đi về ô xuất phát chỉ bằng một nước đi. Yêu cầu: Viết chương trình xếp n quân hậu vào bàn cờ vua n×n (4 ≤ n ≤ 16) sao cho không có quân hậu nào ăn quân hậu nào. trong đó có Gauss và Georg Cantor.n dòng tiếp theo.x dòng tiếp theo. S. chính xác là 26. và sau đó nhiều nhà toán học. Dữ liệu kết quả được ghi ra tập tin LUANLUU. Dữ liệu đọc vào từ tập tin LUANLUU. Quân mã được đặt ở một ô trên một bàn cờ trống nó phải di chuyển theo quy tắc của cờ vua để đi qua mỗi ô trên bàn cờ đúng một lần.INP: . Một hành trình như vật được gọi là hành trình đóng. Glaisher hoàn chỉnh phương pháp này. gồm k cầu thủ. có các công trình về bài toán này và tổng quát nó thành bài toán xếp hậu.OUT: .728. xuất ra tất cả các cách đặt quân hậu thoả yêu cầu đề bài. Gunther đưa ra phương pháp tìm lời giải bằng cách sử dụng định thức. . Có rất nhiều lời giải cho bài toán này.534. Bài toán này cũng được ứng dụng trong trò chơi máy tính The 7th Guest vào những năm 1990.OUT.821. Những hành trình như vậy được gọi là hành trình mở.Dòng đầu tiên gồm 2 số nguyên n và k. Nauck cũng đã tổng quát bài toán thành bài toán n quân hậu. Bài toán 8 hậu được đưa ra vào 1848 bởi k ỳ thủ Max Bezzel.L. 1824-1872 Bài 6: Bài toán mã đi tuần Mã đi tuần (hay hành trình của quân mã) là bài toán về việc di chuyển một quân mã trên bàn cờ vua (8 x 8). Năm 1874. mỗi dòng là một tên cầu thủ. xuất ra tập tin kết quả các cách chọn cầu thủ cho huấn luyện viên của đội.INP chỉ gồm 1 số nguyên n.Bài 4: Một trận đấu bóng đá phải phân định thắng thua bằng đá luân lưu 11m. Bài 5: Bài toán xếp hậu Trong cờ vua. Hoàng Đăng Quang – Khoa CNTT – HUFLIT . quân hậu là quân có thể ăn một quân khác trên cùng dòng. mỗi dòng là một cách chọn.Dòng đầu tiên là số nguyên x. Dữ liệu được đọc từ tập tin XEPHAU. Dữ liệu kết quả được ghi ra tập tin XEPHAU.

Giả sử đường đi từ vị trí hoàng tử đến vị trí công chúa không tồn tại. 2. m và n đều khác 1 3. Hoàng Đăng Quang – Khoa CNTT – HUFLIT . . Hoàng tử cố gắng tìm đường đi qua mê cung nhanh chóng. không có hành trình đóng nào của quân mã nếu một trong ba điều kiện dưới đây xảy ra: 1.Ở yêu cầu b.Dòng thứ 2 gồm 4 số nguyên. sao cho đến vị trí công chúa là nhanh nhất.OUT.Định lý Schwenk Cho bàn cờ m × n bất kỳ với m nhỏ hơn hoặc bằng n. Các số nguyên chỉ nhận 2 giá trị. mô tả cấu trúc của mê cung. giá trị 0 là đường đi giữa các bức tường. Giả sử đường đi từ vị trí hoàng tử đến vị trí công chúa luôn tồn tại.OUT: .n dòng tiếp theo: mỗi dòng gồm n số nguyên.Dòng đầu tiên chỉ gồm một số nguyên n (1 ≤ n ≤ 100). và vị trí của công chúa. Đường đến gặp công chúa có cấu trúc như một mê cung gồm các bức tường và lối đi giữa các bức tường. a. xuất ra tất cả hành trình đóng và mở của quân mã. là vị trí (dòng. m và n đều là lẻ 2. cột) bắt đầu của hoàng tử. 0 hoặc 1.Ở yêu cầu a. hoặc 4. Giá trị 1 là các bức tường trong mê cung. m = 3 và n = 4.INP chỉ gồm 1 số nguyên n. m = 1. . b. hoặc 8 Hai trong số nhiều hành trình đóng trên bàn cờ 8 x 8 Yêu cầu: Viết chương trình tìm tất cả hành trình đóng và hành trình mở của quân mã trên bàn cờ vua n×n (4 ≤ n ≤ 16). Hoàng tử có một số dụng cụ có thể dùng để phá bức tường nào đó khi cần thiết. 6. Bài 7: Mỗi tối. ghi ra số bức tường của mê cung cần phá đi để đi đến vị trí công chúa.INP: . Dữ liệu kết quả được ghi ra tập tin MECUNG. Yêu cầu: Viết chương trình in ra đường đi từ vị trí bắt đầu của hoàng tử đến vị trí của công chúa trong mê cung. Dữ liệu được đọc từ tập tin MADITUAN. hoàng tử vào tầng hầm của lâu đài để tìm công chúa. là kích thước của mê cung (n×n). Dữ liệu được dọc vào từ tập tin MECUNG. . Dữ liệu kết quả được ghi ra tập tin MADITUAN. lúc này hoàng tử sẽ phải phá một bức tường nào đó của mê cung. chỉ cần ghi ra đường đi từ vị trí hoàng tử đến vị trí công chúa.

phải) của phần tử này { Nếu là ô còn trống { .0).Có thể dùng OpenGL để vẽ ở chế độ đồ hoạ hay có thể hiển thị trò chơi ở chế độ Console.4). (1.2). sj) vào hàng đợi.9). dưới.6). Thuật toán tìm đường đi cho viên bi. hàng đứng (từ trên xuống dưới) hay đường chéo 5 viên bi liên tiếp thì 5 viên bi này sẽ được xóa khỏi bảng.OUT (0.2).MECUNG.Bỏ phần tử này vào Hàng đợi[1] . (4.Nếu phần tử này là ô đích Dừng vòng lặp chính (Thành công) } } } Bỏ tất cả các phần tử của Hàng đợi[1] vào Hàng đợi[0] để tiếp tục loang } Sau khi kết thúc vòng lặp Không tìm thấy đường đi (Thất bại) Hoàng Đăng Quang – Khoa CNTT – HUFLIT . (2. (3. (1.9).9) Bài 8: Trò chơi Line Trò chơi gồm một bảng chứa các viên bi. (1.INP 10 0039 0100000000 0100000000 0101111011 0101000010 0001101110 1111101000 0000101010 0110001000 0111111100 0000000000 MECUNG. fj). Đầu tiên bỏ phần tử đầu tiên (si. (3. (1. Sau đó thực hiện vòng lặp sau để tìm đường đi: Trong khi (Hàng đợi[0] chưa rỗng) { Lấy từng phần tử trong Hàng đợi[0] { Kiểm tra 4 ô xung quanh (trên.2). (4. (1..9). (2. (1. Người chơi sẽ di chuyển vị trí của các viên bi để chúng thành một hàng ngang (từ trái qua phải). (6.0). Yêu cầu: Viết chương trình cài đặt trò chơi Line như trên Hướng dẫn: . Hay có thể dùng thuật giải loang 2 hàng đợi như sau: Giả sử các cần tìm đường đi từ ô (si. tương tự bài số 7.3). sj) đến (fi.5). 0). (3.2). Trò chơi kết thúc khi bảng đầy các viên bi và ghi nhận số điểm cao nhất của người chơi. (1. ….0).7). (4. (4. có 5 loại bi được đặt bằng 5 màu khác nhau. (5.0). trái.1).Lưu lại đường đi từ phần từ này đến vị trí mới (là 1 trong 4 ô xung quanh phần tử hiện tại) .

.Tính ngẫu nhiên: Trong một số trường hợp. Bên cạnh đó. O . Yêu cầu: Viết chương trình mô phỏng trò chơi dò mìn như trên. c.Bài 9: Trò chơi xếp hình (Tetris) Trò chơi sẽ phát sinh ngẫu nhiên các loại gạch như hình bên. Trò chơi kết thúc khi vùng xếp các viên gạch bị đầy không thể xếp vào nữa Các loại gạch của trò chơi xếp hình . một số người chơi còn thắng trực tiếp ngay với 1 click chuột đối với những ván có số lượng mìn thưa thớt (đặc biệt trong trình độ Beginner .Dòng 2: S. Máy chơi tự động. Z Yêu cầu: Viết chương trình mô phỏng trò chơi xếp hình như trên. Nếu không may trúng phải ô có mìn (điều này ít xảy ra hơn) thì người chơi trò chơi kết thúc. các dòng được xếp đầy sẽ được cắt bỏ.Người chơi khởi đầu với một bảng ô vuông trống thể hiện "bãi mìn". số dòng cắt cùng lúc càng nhiều người chơi ghi điểm càng nhiều. Hoàng Đăng Quang – Khoa CNTT – HUFLIT . a. không thể xác định chính xác vị trí của mìn chỉ dựa vào những con số gợi ý. người chơi sẽ sử dụng các viên gạch này (có thể quay 90o. b. Số trên một ô là chỉ số ô có mìn trong cả thảy 8 ô nằm lân cận với ô đó. người chơi đánh dấu vào ô đó bằng hình lá cờ (click chuột phải). . J. do đó trò chơi mang tính may rủi.Trò chơi kết thúc với phần thắng dành cho người chơi nếu tìm được tất cả các ô có mìn và mở được tất cả các ô không có mìn. . L. T. 1 người chơi.-Tính logic: Trò chơi dựa trên cơ sở tính toán sự phân phối (trong không gian) các quả mìn dựa trên dữ kiện là các con số hiện ra (chỉ số lượng quả mìn kề với một ô).Click chuột vào một ô vuông trong bảng. người với máy) Bài 10: Trò chơi dò mìn Mục tiêu Trong Dò mìn. Trường hợp thường xảy ra hơn là ô đó không có mìn và một vùng các ô sẽ được mở ra cùng với những con số.Nếu chắc chắn một ô có mìn. Tính chất . 2 người chơi cùng lúc (người với người.Dòng 1: I. người chơi phải tìm được vị trí chính của tất cả các quả mìn rải ngẫu nhiên trên một bảng ô vuông. . 180o…) sắp sao cho đầy 1 hay nhiều hàng ngang.dễ). Cách chơi .

Đăng ký hàm callback OnDraw để vẽ (đường thẳng) glutDisplayFunc(OnDraw). //7. 100). char* argv[]) { //1. Tạo cửa sổ OpenGL (Create Window) Tạo Project trong Visual C++ File New Projects Win32 Console Application project OK A simple application Finish OK • • Chọn Location và đặt tên cho Chép các tập tin của thư viện OpenGL vào thư mục chứa Project • Thiết lập các thông số cho Project Project Settings (Alt+F7) C/C++ precomplied headers OK. 100) glutInitWindowPosition(100. return 0. Thiết lập chế độ hiển thị glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB). Bắt đầu vòng lặp sự kiện của glut glutMainLoop(). Tạo cửa sổ có title là ‘Create OpenGL Window’ glutCreateWindow("Create OpenGL Window"). //4. } Hoàng Đăng Quang – Khoa CNTT – HUFLIT . //2. argv). //3. //6. Khởi tạo glut glutInit(&argc. 250). int main(int argc. //5.h" void OnDraw().HƯỚNG DẪN LẬP TRÌNH ĐỒ HOẠ VỚI THƯ VIỆN OPENGL (Open Graphics Language) 1. Thiết lập kích thước cửa sổ (250x250) glutInitWindowSize(250. Category: Precompiled Headers Not using #include "glut. Thiết lập vị trí của sổ (100.

glutReshapeFunc(OnReShape). ta sẽ chuyển cửa sổ màn hình về dạng như sau: (MaxX.0) (0. 0).0. } (1.0) Như trong hình trên ta thấy gốc toạ độ ở giữa tâm màn hình.1.0. glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB). -1. argv). //Vẽ đường thẳng glColor3ub(0.1. Xuất kết quả ra màn hình (-1. return 0.0) glFlush().0. MaxY) (0.0.void OnDraw() { //1.0) Viết lại chương trình trên như sau: #include "glut.0. } Hoàng Đăng Quang – Khoa CNTT – HUFLIT . 1. glutMainLoop(). //Toạ độ điểm 1 glVertex2f(1. glClear(GL_COLOR_BUFFER_BIT). //3.0. char* argv[]) { glutInit(&argc.h" void OnReShape(int Width. glutInitWindowPosition(100.0 đến 1.0).1. int main(int argc. //Thiết lập màu đen glVertex2f(-1. trục hoành và trục tung chạy từ -1. glutInitWindowSize(250.0). 100). Vẽ đường thẳng với màu đen glBegin(GL_LINES). //2. glutCreateWindow("Create OpenGL Window").int Height). glutDisplayFunc(OnDraw).0. Xoá màn hình với màu trắng glClearColor(1. //Toạ độ điểm 2 glEnd(). 250). 0.0). 0. 0. -1. Để thuận tiện cho việc làm các bài tập ở trên.

Với các prototype như sau: void OnMouse(int button. 0. int x. //2. //2.int Height) { glViewport(0.…) glutSpecialFunc(OnKeySpecialDown). void OnKeyDown(unsigned char Key. //3. Gọi hàm OnKeyDown khi nhấn một ký tự ASCII glutKeyboardFunc(OnKeyDown). int y). Gọi hàm OnKeyUp khi thả một ký tự ASCII glutKeyboardUpFunc(OnKeyUp). //3. (GLdouble)Width. 0).void OnReShape(int Width.int x. Gọi hàm OnKeySpecialDown khi nhấn một phím đặc biệt (các phím mũi tên.int x. glMatrixMode(GL_PROJECTION). //4.int x. void OnPassiveMotion (int x. gluOrtho2D(0. Gọi hàm OnMouseMotion khi phím chuột đang được nhấn và di chuyển glutMotionFunc(OnMouseMotion). } 3. int y). Hoàng Đăng Quang – Khoa CNTT – HUFLIT . (GLdouble)Width. Gọi hàm OnPassiveMotion khi phím chuột không được nhấn và di chuyển glutPassiveMotionFunc(OnPassiveMotion). nhưng phải đúng kiểu dữ liệu) void OnKeyDown(unsigned char Key. Gọi hàm OnMouse khi có phím chuột được nhấn glutMouseFunc(OnMouse). void OnKeySpecialUp(int Key. void OnKeySpecialDown(int Key. (GLdouble)Height.int x. int y). (GLsizei)Width. 0 . int y). Xử lý xự kiện bàn phím (Keyboard event handler) Sử dụng các hàm callback sau: //1. int state. //Nếu muốn gốc toạ độ nằm ở góc trên bên trái thì viết lại là // gluOrtho2D(0. } 2. (GLsizei)Height). int y) { if (Key == 27) //Key == Esc ? exit(0).int y). int y). Với các prototype sau: (tên hàm và tên biến có thể đặt khác. (GLdouble)Height). void OnMouseMotion(int x. Xử lý sự kiện chuột (Mouse event handler) Sử dụng các hàm callback sau: //1. Gọi hàm OnKeySpecialUp khi thả một phím đặc biệt glutSpecialUpFunc(OnKeySpecialUp). void OnKeyUp(unsigned char Key. F1 F12.int x. glLoadIdentity(). int y).

%d) ". //Xuất toạ độ (x. return Color. blue) ColorEntry(int r. int x. #define GREEN 0x00FF00. int g. Xử lý màu sắc (Color) struct ColorEntry { unsigned char red.x. //2. Color = Color | green. unsigned char green. Color = Color | red. #define MAGENTA 0xFF00FF. #define GRAY 0xC0C0C0. Hàm tạo màu từ giá trị kiểu nguyên (0x00000: black. . 0xFFFFFF: white) ColorEntry(int Color) { blue = Color & 0xFF. unsigned char blue. Color = Color | blue.y). Color = Color << 8. #define YELLOW 0xFFFF00. green. Hoàng Đăng Quang – Khoa CNTT – HUFLIT #define CYAN 0x00FFFF. y) của chuột khi một phím chuột được nhấn } 4. int b):red(r). } }. //1.int y) { if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { //Xử lý khi nhấn phím trái chuột } } void OnMouseMotion(int x. #define RED 0xFF0000. Hàm tạo màu từ 3 thành phần màu cơ bản (red. Color = Color << 8. #define BLUE 0x0000FF. green = (Color >> 8) & 0xFF. } //3.void OnMouse(int button. blue(b){}. Hàm chuyển từ ColorEntry sang kiểu int int ToInt() { int Color. #define WHITE 0xFFFFFF. int y) { printf("(%d. red = (Color >> 16) & 0xFF. Định nghĩa một số màu thông dụng #define BLACK 0x000000. int state.green(g).

chung một cạnh Vẽ một tập hợp các tứ giác liền nhau.green.0) { ColorEntry c = ColorEntry(Color). int y.blue).red.y1). blue). double Size=1. glColor3ub(red.yn). Vẽ một số hình cơ bản (Draw shapes) glBegin(<Hằng số>). } Hoàng Đăng Quang – Khoa CNTT – HUFLIT Ý nghĩa Vẽ điểm Vẽ đường thẳng nối hai điểm Tập hợp của những đoạn đựơc nối với nhau Đường gấp khúc khép kín Vẽ hình tam giác Vẽ tứ giác Vẽ một tập hợp các tam giác liền nhau.c. …. glColor3ub(c.c. green. chung một cạnh Vẽ hình quạt . glEnd(). glVertex2f(xn.5. glEnd(). glBegin(GL_POINTS).y). Hằng số GL_POINT GL_LINES GL_LINE_STRIP GL_LINE_LOOP GL_TRIANGLES GL_QUADS GL_TRIANGLES_STRIP GL_QUAD_STRIP GL_TRIANGLE_FAN Một số hàm vẽ: void PutPixel(int x. int Color. glVertex2f(x1. glVertex2f(x. glPointSize(Size).

y1 + b.y=b.y2). glColor3ub(c.int y2. b2. glVertex2f(x1.y1 .void DrawLine(int x1. glBegin(GL_LINE_STRIP).red.y2).blue). int y1.Color). glVertex2f(x2. y--. int y1. int b.blue). glVertex2f(x2. a2 = a*a. int Color) { long x=0. else p+=2*a2+4*a2*(y-1)-4*b2*(x+1). x++. } Hoàng Đăng Quang – Khoa CNTT – HUFLIT . } else { if (p<=b2/2) p+=2*a2+4*a2*(y-1). float p.c. glVertex2f(x1. glEnd().y1).green.y2). glVertex2f(x1. int Color) { ColorEntry c = ColorEntry(Color). } void DrawRectangle(int x1. PutPixel(x1. int Color) { ColorEntry c = ColorEntry(Color). int a2.red. glColor3ub(c.c. int x2.c. glEnd(). y--. else p+=2*b2*(3+2*x)-4*a2*(y-1).int y2. glBegin(GL_LINES). int y1. p=2*b2+a2*(1-2*b).green. glVertex2f(x1.b. int x2.y1). } void DrawEllipse(int x1.Color). glVertex2f(x2. x++. while (y>0) { if (b2*x<a2*y) { if (p<=a2/2) p+=2*b2*(3+2*x). int a. b2 = b*b.y1). PutPixel(x1.y1).c.

} Một số hàm tô: void FillRectangle(int x1. PutPixel(x1 . float p. a2 = a*a.y1). b2 = b*b.int y2.red. while (y>0) { if (b2*x<a2*y) { if (p<=a2/2) p+=2*b2*(3+2*x). glColor3ub(c.y2).c. glEnd().y1 + y. int Color) { ColorEntry c = ColorEntry(Color).y2).c.blue).blue). glVertex2f(x1.Color). glVertex2f(x2. glColor3ub(c.y1 + y. int y.r. else p+=2*b2*(3+2*x)-4*a2*(y-1). int b. int r.green.Color). PutPixel(x1 .Color). } Hoàng Đăng Quang – Khoa CNTT – HUFLIT . ColorEntry c = ColorEntry(Color).c. x++. glBegin(GL_POLYGON).y. int Color) { long x=0.red.x.y1 .y1).Color).y.x. glVertex2f(x1. int y1.Color). y--.c. int a. p=2*b2+a2*(1-2*b). int Color) { DrawEllipse(x.y1). } void FillEllipse(int x1. int y1. int a2.PutPixel(x1 + x. } } void DrawCircle(int x. glVertex2f(x2.r.y. int x2.y=b. b2. glBegin(GL_POINTS). PutPixel(x1 + x.green.y1 . glVertex2f(x1.

} 6. Xử lý bàn phím song song Để xử lý phím song song (nhiều phím cùng lúc). int r. Xử lý song song 7. Color). (x.y1 + y.int x. OnTime. x1 + x. } Hoàng Đăng Quang – Khoa CNTT – HUFLIT . Color).x. msecs: Chờ khoảng thời gian (milliseconds) trước khi gọi hàm OnTime lần đầu tiên value: giá trị tham số truyền cho hàm OnTime void OnTime(int Value) { //1.1. r. int y. } void DrawCircle(int x.y1 . //Key: Mã phím. 256 phần tử đầu cho các phím ký tự ASCII //và 256 phần tử sau cho các phím đặc biệt bool KeyPress[512]. Các xử lý khi hàm OnTime được gọi …………… //2. x1 + x. Gọi lại hàm OnTime sau một khoảng thời gian là Value glutTimerFunc(0. Value).else { if (p<=b2/2) p+=2*a2+4*a2*(y-1). y. y--.y. else p+=2*a2+4*a2*(y-1)-4*b2*(x+1). x++. value).x.y1 . r. Xử lý thời gian Sử dụng hàm callback: glutTimerFunc(msecs. y) toạ độ của chuột khi nhấn phím void OnKeyDown(unsigned char Key. } DrawLine(x1 .OnTime. } 7.y1 + y. có thể khai báo mảng sau: //Mảng kiểm tra một phím có được nhấn hay không. int Color) { FillEllipse(x.y. int y) { KeyPress[Key] = true. } glEnd(). DrawLine(x1 . Color).

int y) { KeyPress[Key] = false. int Width. } void OnKeySpecialUp(int Key. Xử lý nhiều đối tượng song song Để xử lý nhiều đối tượng song song (ví dụ để di chuyển nhiều đối tượng với tốc độ khác nhau) với một hàm xử lý thời gian duy nhất. các phím đặc biệt thì cộng thêm 256) #define KEY_ESC 27 #define KEY_UP 357 #define KEY_DOWN 359 #define KEY_LEFT 356 #define KEY_RIGHT 358 #define KEY_END 363 #define KEY_F1 257 #define KEY_F2 258 Ở bất kỳ hàm nào trong chương trình. y): toạ độ của đối tượng //(dx. 7. để đếm số đơn vị thời gian.int x. int y) { //Các phím đặc biệt được cộng với 256 để phân biệt với các ký tự ASCII KeyPress[Key+256] = true. Trước khi xử lý phím.int x. dùng lệnh printf(“%d”.int x. chỉ cần kiểm tra giá trị trong mảng KeyPress tại vị trí là mã của phím đó như sau: if (KeyPress[KEY_ESC]) exit(0).2.void OnKeyUp(unsigned char Key. mỗi đối tượng sẽ có thêm một biến đếm (TickCount). dy. int Color. khi số đơn vị thời gian của một đối tượng bằng với một giá trị nào đó (MaxTickCount). } void OnKeySpecialDown(int Key. int y) { KeyPress[Key+256] = false. int dx. } Định nghĩa một số mã của một số phím đặc biệt: (để biết mã của các phím. class Shape { int x. sizeof(int)*512). 0. int TickCount. để kiểm tra một phím có được nhấn hay không.Key). phải gán tất cả các phần tử của mảng KeyPress về trạng thái false như sau: memset(KeyPress. int MaxTickCount. dy): độ dịch của đối tượng //chiều rộng của đối tượng //màu sắc của đối tượng //biến đếm số đơn vị thời gian //số đơn vị thời gian Hoàng Đăng Quang – Khoa CNTT – HUFLIT . y. //(x. ta sẽ thực hiện việc di chuyển đối tượng. trong các hàm xử lý bàn phím.

TickCount = 0. x+= dx.public: Shape(int x. this->Color = Color.20. } } void Move() { int MaxX = glutGet(GLUT_WINDOW_WIDTH). } void Process() { TickCount++. glutReshapeFunc(OnReShape). Color). this->Width = Width. int MaxTickCount) { this->x = x. glutKeyboardFunc(OnKeyDown). if (x > MaxX) x = 0. Hoàng Đăng Quang – Khoa CNTT – HUFLIT . this->dy = dy.160. } }.0. y+= dy. Đăng ký 3 hàm callback sau: glutDisplayFunc(OnDraw). x+Width.2. this->y = y. this->MaxTickCount = MaxTickCount. } void Draw() { DrawRectangle(x. y+Width. int dx. if (y > MaxY) y = 0. int Color.2. int dy. 0x00FF00.200.y. 10). int MaxY = glutGet(GLUT_WINDOW_HEIGHT).0. if (TickCount >= MaxTickCount) { TickCount = 0. Tạo 2 đối tượng của lớp Shape: Shape a(0. 40. 5). Shape b(0. int y. Move(). this->dx = dx. 0xFF0000. int Width.

char* argv[]) { glutInit(&argc. Trong hàm main(): int main(int argc. } void OnReShape(int Width.(GLsizei)Height). glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB). glMatrixMode(GL_PROJECTION). } void OnDraw() { glClear(GL_COLOR_BUFFER_BIT). b. glutMainLoop(). glutTimerFunc(0.0.Process(). b. Như vậy người xem sẽ không thấy quá trình vẽ.0.int Height) { glViewport(0. 0. 500). ta dùng 2 vùng đệm màn hình để vẽ. 100). và ngược lại.void OnTime(int Value) { a. glutSwapBuffers(). } 8.Draw(). Double Buffering Để kết quả vẽ trên màn hình không bị giựt hay chớp tắc. gluOrtho2D(0. glLoadIdentity(). xuất vùng đệm 2.Process(). return 0. chỉ thấy kết quả sau cùng. glClearColor(0. (GLsizei)Width.Value). 0 . argv). Vẽ vào vùng đệm 1. //Vẽ gì đó ở đây glFlush(). glutInitWindowPosition(100.0).(GLdouble)Height). glFlush(). } Hoàng Đăng Quang – Khoa CNTT – HUFLIT .Draw().OnTime.(GLdouble)Width. glutInitWindowSize(500. a. glutCreateWindow("Create OpenGL Window"). } void OnDraw() { glClear(GL_COLOR_BUFFER_BIT). OnDraw().

Sign up to vote on this title
UsefulNot useful