Đề 2017-2018
Đầu những năm 1900, dựa trên sự hiện diện của các kháng nguyên trên màng hồng cầu, các nhà khoa học đã xác định rằng con người có 4 nhóm máu khác nhau: O, A, B và AB. Hệ thống phân loại nhóm máu này (gọi là hệ thống nhóm máu ABO) cung cấp cho bác sĩ các thông tin quan trọng để lựa chọn nhóm máu phù hợp trong việc truyền máu. Và đồng thời có thể tiên đoán được nhóm máu tương đối của người con dựa trên nhóm máu của cha mẹ theo cơ chế di truyền học.
Nhóm máu của người con khi biết được nhóm máu của cha và mẹ:

Ngoài ra còn có thêm hệ thống phân loại Rh (Rhesus)
Căn cứ vào sự khác biệt khi nghiên cứu về sự vận chuyển oxy của hồng cầu thì các hồng cầu có thể mang ở mặt ngoài một protein gọi là Rhesus. Nếu có kháng nguyên D thì là nhóm Rh+ (dương tính), nếu không có là Rh- (âm tính). Các nhóm máu A, B, O, AB mà Rh- thì được gọi là âm tính A-, B-, O-, AB-. Nhóm máu Rh- chỉ chiếm 0,04% dân số thế giới. Đặc điểm của nhóm máu Rh này là chúng chỉ có thể nhận và cho người cùng nhóm máu, đặc biệt phụ nữ có nhóm máu Rh- thì con rất dễ tử vong.
Người có nhóm máu Rh+ chỉ có thể cho người cũng có nhóm máu Rh+ và nhận người có nhóm máu Rh+ hoặc Rh-
Người có nhóm máu Rh- có thể cho người có nhóm máu Rh+ hoặc Rh- nhưng chỉ nhận được người có nhóm máu Rh- mà thôi
Trường hợp người có nhóm máu Rh- được truyền máu Rh+, trong lần đầu tiên sẽ không có bất kỳ phản ứng tức thì nào xảy ra nhưng nếu tiếp tục truyền máu Rh+ lần thứ 2 sẽ gây ra những hậu quả nghiêm trọng do tai biến truyền máu. Tương tự với trường hợp mẹ Rh- sinh con (lần đầu và lần thứ hai trở đi).

Áp dụng kiến thức lập trình hướng đối tượng (kế thừa, đa hình) thiết kế sơ đồ chi tiết các lớp đối tượng (1.5 điểm) và xây dựng chương trình thực hiện các yêu cầu sau:
- Nhập danh sách các nhóm máu của một nhóm người. (1 điểm)
- Cho một bộ 3 nhóm máu của 3 người là cha, mẹ, con. Hãy kiểm tra và đưa ra kết quả nhóm máu có phù hợp với quy luật di truyền hay không? (1 điểm)
- Chọn một người X trong danh sách. Hãy liệt kê tất cả các người còn lại trong danh sách có thể cho máu người X này. (1 điểm)
Lưu ý: Trong trường hợp sinh viên không biết về nhóm máu và di truyền học trước đây thì phải đọc kỹ thông tin trên (các thông tin trên đủ để sinh viên thực hiện các yêu cầu của đề thi) và nghiêm túc làm bài. Giám thị coi thi không giải thích gì thêm.
Phân tích đề
Bước 1: Xác định được các lớp đối tượng có trong đề bài
Từ các câu hỏi, ta có thể thấy vấn đề cần giải quyết đều liên quan tới danh sách nhóm máu của từng người. Vì vậy, ta nên thiết kế một lớp DanhSach để phục vụ cho việc giải quyết các vấn đề ở từng câu.
Vì đối tượng chủ yếu được đề cập trong đề thi là các nhóm máu(tổng quát) mà cụ thể là những nhóm A, B, AB , O. Cùng với mục đích áp dụng kiến thức về kế thừa, đa hình, ta sẽ xây dựng một lớp NhomMau là lớp cơ sở còn những lớp A, B, AB, O là các lớp dẫn xuất kế thừa từ lớp NhomMau.
Bước 2: Thiết kế thuộc tính
Đối với lớp DanhSach, thuộc tính cần thiết là một mảng con trỏ đối tượng thuộc lớp NhomMau để gọi thực hiện các phương thức ảo theo cơ chế đa xạ (có thể lựa chọn cấp phát động, cấp phát tĩnh,…)
Từ các thông tin của đề thi, ta có thể xác định các thuộc tính chung của các nhóm máu là Rh và các lớp con không có thuộc tính riêng nào.
Bước 3: Thiết kế phương thức:
Từ các câu hỏi, ta xác định được những phương thức cần định nghĩa ở lớp DanhSach là phương thức nhập thông tin; phương thức kiểm tra theo quy luật di truyền để xác định nhóm máu của cha, mẹ, con có phù hợp hay không; phương thức kiểm tra xem nhóm máu người cho có phù hợp với người nhận. Đây là các phương thức nên được ưu tiên định nghĩa trước.
Ở lớp NhomMau, ta cần định nghĩa các phương thức để hỗ trợ cho việc định nghĩa các hàm thành phần của lớp DanhSach:
- Phương thức nhập thông tin: Cụ thể hơn là nhập Rh, không cần nhập tên nhóm máu A, B, AB hay O vì các thông tin này sẽ được nhập khi gọi thực hiện phương thức nhập thông tin của lớp
DanhSach. Ở lớp cha, phương thức này là phương thức không ảo vì các lớp con không có thuộc tính riêng(không cần ghi đè). - Các phương thức kiểm tra: Do các quy luật ứng với từng nhóm máu là khác nhau nên những phương thức có nhiệm vụ tương tự cần được định nghĩa lại ở các lớp dẫn xuất. Vì vậy, ở lớp
NhomMau, các phương thức kiểm tra nên là phương thức thuần ảo. - Các phương thức truy vấn: Hai thông tin cần được truy vấn để hỗ trợ cho việc định nghĩa các phương thức kiểm tra là tên nhóm máu và Rh. Phương thức truy vấn đến Rh là phương thức không ảo vì lí do tương tự phương thức nhập thông tin. Ngược lại, phương thức truy vấn đến tên nhóm máu nên là phương thức thuần ảo để giá trị trả về là loại máu của đối tượng đang gọi thực hiện phương thức.
Ở các lớp dẫn xuất, việc định nghĩa lại các phương thức truy vấn chỉ đơn giản là trả về tên nhóm máu của lớp (có thể là kiểu kí tự hoặc kiểu dữ liệu khác theo quy ước mỗi người). Để định nghĩa các phương thức kiểm tra, ta cần đọc kĩ thông tin của đề kết hợp với kiến thức bản thân để tìm ra những cách kiểm tra tổng quát nhất theo từng quy luật. Sau đây là một số nhận định rút ra từ đề thi giúp ta định nghĩa những phương thức này nhanh hơn:
- Nếu cha hoặc mẹ có nhóm máu A hoặc AB thì con có thể có nhóm máu A.
- Nếu cha hoặc mẹ có nhóm máu B hoặc AB thì con có thể có nhóm máu B
- Nếu cha và mẹ có nhóm máu AB thì con không thể có nhóm máu O
- Nếu cha và mẹ có nhóm máu giống nhau nhưng khác nhóm máu AB hoặc một trong hai người có nhóm máu O thì con không thể có nhóm máu AB
- Người có nhóm máu A chỉ nhận được nhóm máu A và O(chưa xét Rh)
- Người có nhóm máu B chỉ nhận được nhóm máu B và O(chưa xét Rh)
- Người có nhóm máu O chỉ nhận được nhóm máu O(chưa xét Rh)
- Người có nhóm máu AB có thể nhận được tất cả nhóm máu (chưa xét Rh)
- Người có nhóm máu Rh+ có thể nhận nhóm máu Rh+ hoặc Rh-
- Người có nhóm máu Rh- có thể cho người có nhóm máu Rh+ hoặc Rh-
Sơ đồ thiết kế lớp
Lời giải tham khảo
#include<iostream>
using namespace std;
class NhomMau {
protected:
char Rh;
public:
void Nhap();/*Các lớp con không có thuộc tính riêng nên hàm Nhap() không cần là phương thức ảo*/
virtual char getMau() = 0;/*Lấy nhóm máu của các lớp con(không xét Rh) để thực hiện các phương thức kiểm tra*/
char getRh();//Tương tự như phương thức Nhap()
virtual bool KiemTraChaMe(NhomMau*, NhomMau*) = 0;
//Kiểm tra xem nhóm máu cha mẹ có phù hợp với nhóm máu của conn
virtual bool KiemTraTuongThich(NhomMau*) = 0;
//Kiểm tra nhóm máu người cho có phù hợp
};
class DanhSach { // Lớp DanhSach dùng để quản lí các nhóm mau
protected:
int n; // Số lượng loại máu
NhomMau* ds[100]; //Mảng ds dùng để quản lí nhóm máu của từng người
public:
void Nhap(); //Nhập danh sách nhóm máu của từng người
bool KiemTraDiTruyen(); //Kiểm tra nhóm máu của cha, mẹ, con
void TimKiemNguoiCho(int); //Xuất ra chỉ số của những người cho phù hợp
};
class A : public NhomMau {
public:
char getMau();
bool KiemTraChaMe(NhomMau*, NhomMau*);
bool KiemTraTuongThich(NhomMau*);
};
class B : public NhomMau {
public:
char getMau();
bool KiemTraChaMe(NhomMau*, NhomMau*);
bool KiemTraTuongThich(NhomMau*);
};
class O : public NhomMau {
public:
char getMau();
bool KiemTraChaMe(NhomMau*, NhomMau*);
bool KiemTraTuongThich(NhomMau*);
};
class AB : public NhomMau {
public:
char getMau();
bool KiemTraChaMe(NhomMau*, NhomMau*);
bool KiemTraTuongThich(NhomMau*);
};
//Định nghĩa các phương thức cho câu 1
void NhomMau::Nhap() {
cout << "Nhap Rh: ";
cin >> Rh;
}
void DanhSach::Nhap() {
cout << "Nhap so luong nguoi: ";
cin >> n;
for (int i = 0; i < n; i++) {
char loai;
cout << "Nhap loai mau (nhap C thay cho AB) thu [" << i << "]: ";
cin >> loai;
switch (loai) {
case 'A':
ds[i] = new A;
break;
case 'B':
ds[i] = new B;
break;
case 'O':
ds[i] = new O;
break;
case 'C':
ds[i] = new AB;
}
ds[i]->Nhap();
}
}
//Định nghĩa các phương thức cho câu 2
char A::getMau() {
return 'A';
}
char B::getMau() {
return 'B';
}
char O::getMau() {
return 'O';
}
char AB::getMau() {
return 'C';
}
char NhomMau::getRh() {
return Rh;
}
bool A::KiemTraChaMe(NhomMau* cha, NhomMau* me) {
char mauCha = cha->getMau();
char mauMe = me->getMau();
//Nếu cha hoặc mẹ có nhóm máu A hoặc AB thì con có thể có nhóm máu A
if (mauCha == 'A' || mauCha == 'C' || mauMe == 'A' || mauMe == 'C') {
return true;
}
return false;
}
bool B::KiemTraChaMe(NhomMau* cha, NhomMau* me) {
char mauCha = cha->getMau();
char mauMe = me->getMau();
//Nếu cha hoặc mẹ có nhóm máu B hoặc AB thì con có thể có nhóm máu B
if (mauCha == 'B' || mauCha == 'C' || mauMe == 'B' || mauMe == 'C') {
return true;
}
return false;
}
bool O::KiemTraChaMe(NhomMau* cha, NhomMau* me) {
char mauCha = cha->getMau();
char mauMe = me->getMau();
//Nếu cha và mẹ có nhóm máu AB thì con không thể có nhóm máu O
if (mauCha == 'C' && mauMe == 'C') {
return false;
}
return true;
}
bool AB::KiemTraChaMe(NhomMau* cha, NhomMau* me) {
char mauCha = cha->getMau();
char mauMe = me->getMau();
/*Các trường hợp con không thể có nhóm máu AB:
- Cha và mẹ có nhóm máu giống nhau nhưng khác nhóm máu AB
- Cha hoặc mẹ có nhóm máu O*/
if ((mauCha == mauMe && mauMe != 'C') || mauCha == 'O' ||mauMe == 'O') {
return false;
}
return true;
}
bool DanhSach::KiemTraDiTruyen() {
int cha, me, con;
cout << "Nhap lan luot chi so cua cha, me, con: ";
cin >> cha >> me >> con;
bool check = ds[con]->KiemTraChaMe(ds[cha], ds[me]);
if (check == 1) {
return true;
}
return false;
}
//Định nghĩa các phương thức cho câu 3
void DanhSach::TimKiemNguoiCho(int x) {
for (int i = 0; i < n; i++) {
if (i != x && ds[x]->KiemTraTuongThich(ds[i]) == 1) {
cout << i << endl;
}
}
}
bool A::KiemTraTuongThich(NhomMau* nguoiCho) {
//Người có nhóm máu A chỉ nhận được nhóm máu A và O(chưa xét Rh)
if (nguoiCho->getMau() == 'A' || nguoiCho->getMau() == 'O') {
//Người có nhóm máu Rh+ có thể nhận nhóm máu Rh+ hoặc Rh-
//Người có nhóm máu Rh- có thể cho người có nhóm máu Rh+ hoặc Rh-
if (this->getRh() == '+' || nguoiCho->getRh() == '-') {
return true;
}
}
return false;
}
bool B::KiemTraTuongThich(NhomMau* nguoiCho) {
//Người có nhóm máu B chỉ nhận được nhóm máu B và O(chưa xét Rh)
if (nguoiCho->getMau() == 'O' || nguoiCho->getMau() == 'B') {
if (this->getRh() == '+' || nguoiCho->getRh() == '-') {
return true;
}
}
return false;
}
bool O::KiemTraTuongThich(NhomMau* nguoiCho) {
//Người có nhóm máu O chỉ nhận được nhóm máu O(chưa xét Rh)
if (nguoiCho->getMau() == 'O') {
if (this->getRh() == '+' || nguoiCho->getRh() == '-') {
return true;
}
}
return false;
}
bool AB::KiemTraTuongThich(NhomMau* nguoiCho) {
//Người có nhóm máu AB có thể nhận được tất cả nhóm máu (chưa xét Rh)
if (this->getRh() == '+' || nguoiCho->getRh() == '-') {
return true;
}
return false;
}