Đề HK2 2023-2024
Có rất nhiều chủng virus sống trên các vật chủ là các sinh vật sống khác kể cả loài người. Khi nhiễm lên vật chủ, một loại virus có thể gây hậu quả từ tử vong, các triệu chứng nặng, các triệu chứng nhẹ hoặc không triệu chứng gì tùy thuộc vào khả năng miễn dịch của vật chủ.
- Khả năng miễn dịch của vật chủ đối với các loại virus ở ba mức: thấp, trung bình và cao. Nó sẽ ảnh hưởng đến xác suất gặp các triệu chứng nặng, nhẹ, không có triệu chứng hoặc xác suất tử vong như bảng sau:
| Không triệu chứng | Triệu chứng nhẹ | Triệu chứng nặng | Tử vong | |
|---|---|---|---|---|
| Cao | 50% | 35% | 15% | 50% |
| Trung bình | 10% | 40% | 50% | 70% |
| Thấp | 5% | 15% | 80% | 100% |
- Xác suất tử vong ở bảng trên được hiểu là xác suất dựa trên xác suất tử vong trung bình của từng loại virus. Chẳng hạn có một virus có xác suất tử vong trung bình là 50% và nhiễm lên vật chủ có khả năng miễn dịch trung bình thì xác suất tử vong cuối cùng sẽ là : 70% x 50% = 35%
Trong những năm 2019 đến nay, dòng virus Corona mới - tên chính thức là SARS-CoV-2 xuất hiện đã gây ra bao hậu quả tàn khốc cho khắp thế giới với hơn chục triệu người nhiễm bệnh và hơn nửa triệu người tử vong. Triệu chứng nhẹ của Covid-19 giống như cảm cúm thông thường như sốt, ho, mất vị giác trong vài ngày rồi khỏi. Triệu chứng nặng của Covid-19 bao gồm sốt cao, ho khan, khó thở và đôi lúc kèm theo đau đầu dữ dội. Tuy nhiên, Covid-19 gây tử vong với xác suất trung bình khá thấp là 3-5%.
Một dòng virus chết chóc khác là Ebola, có khả năng gây tử vong với tỷ lệ trung bình lên đến 50%. Triệu chứng nhẹ của Ebola trong vòng 2-3 tuần đầu bao gồm sốt, đau họng, đau cơ và đau đầu. Sau đó, nếu không may, người bệnh có thể gặp các triệu chứng nặng như bị nôn mửa, tiêu chảy và nặng nhất là xuất huyết cả ngoài lẫn bên trong.
Virus HIV tấn công hệ miễn dịch của con người, làm suy yếu nó để các virus/bệnh khác có cơ hội trỗi dậy. HIV chỉ gây các triệu chứng nhẹ giống cảm sốt trong thời gian ủ bệnh (từ 3 đến 5 năm). Nếu không may mắn được miễn dịch, đến giai đoạn AIDS, giai đoạn hệ miễn dịch đã bị suy yếu đáng kể, người bệnh sẽ gặp các triệu chứng nặng như mệt mỏi cực độ không giải thích được, sưng hạch kéo dài, lở loét, viêm phổi, tiêu chảy nặng và sau đó là khả năng tử vong trung bình cao đến 90%.
Hậu quả do virus gây ra (bao gồm cả tử vong) khi bị nhiễm có thể được giảm bớt bằng cách tiêm vaccine phòng ngừa. Nếu tiêm vaccine sẽ làm thay đổi xác suất gặp triệu chứng nặng/nhẹ/không triệu chứng/tử vong như sau (theo chiều hướng có lợi).
| Không triệu chứng | Triệu chứng nhẹ | Triệu chứng nặng | Tử vong | |
|---|---|---|---|---|
| Cao | 70% | 25% | 5% | 40% |
| Trung bình | 20% | 50% | 30% | 60% |
| Thấp | 10% | 40% | 50% | 80% |
-
Áp dụng kiến thức Lập trình hướng đối tượng (kế thừa, đa hình), vẽ sơ đồ chi tiết các lớp đối tượng được mô tả trong đề bài (1.5đ).
Viết chương trình mô phỏng thực nghiệm dịch tể học thực hiện các yêu cầu sau:
-
Nhập danh sách
Nvật chủ(N <= 10000). Mỗi vật chủ được gắn mã số (là một chuỗi) để nhận diện theo dõi. Sau đó, cho từng vật chủ trong danh sách nhiễm tất cả 3 chủng virus kể trên (Covid-19, Ebola, HIV). (1.5đ). -
In ra mã các vật chủ và mô tả các triệu chứng của từng vật chủ (nếu có), cũng như cho biết vật chủ có bị tử vong hay không. (1.0đ).
-
Nhập danh sách
Mvật chủ mới (trong đóM = Ntrước đó). Tiêm vaccine cho cả 3 loại virus. Cho từng vật chủ nhiễm cả 3 loại virus. In ra số lượng vật chủ gặp triệu chứng nặng (của bất kỳ virus nào bị nhiễm), số lượng vật chủ tử vong. (1.0đ).
Phân tích đề
Bước 1: Xác định các lớp
Khi đọc đề, ta có thể thấy ngay là sẽ có ít nhất là lớp đối tượng VatChu và 3 lớp đối tượng biểu diễn cho 3 loại virus là SARS-CoV-2, Ebola và HIV. Dễ thấy nhất về yếu tố kế thừa và đa hình trong bài này là nằm ở chỗ 3 loại virus có thể kế thừa từ một lớp đối tượng chung là Virus.
Một thành phần nữa cần được chú ý là khả năng miễn dịch của vật chủ. Về cơ bản, các bạn cũng có thể gộp luôn thành phần này là xem chúng như các thuộc tính của vật chủ. Tuy nhiên, nếu các bạn có thể nhận thấy sự lặp lại trong cấu trúc của các khả năng miễn dịch này, và có thể xem chúng như một dạng lớp đối tượng riêng, với các mức độ của khả năng miễn dịch lần lượt được kế thừa từ một lớp cơ sở chung là KhaNangMienDich. Điều này đôi khi có thể giúp chương trình mở rộng dễ dàng khi gặp yêu cầu chi tiết hơn, chẳng hạn như cần bổ sung thên khả năng miễn dịch
Trong bài viết này, chúng mình sẽ làm theo hướng xem khả năng miễn dịch như các lớp đối tượng riêng.
Bước 2: Thiết kế thuộc tính
Ban đầu, chúng ta có thể nghĩ ngay đến những thuộc tính của các đối tượng như sau: lớp đối tượng virus sẽ có thuộc tính xác suất gây tử vong trung bình, khả năng miễn dịch sẽ có những thuộc tính như xác suất gặp triệu chứng.
Tuy nhiên, nếu để ý kĩ thì ta có thể thấy đây là những thuộc tính không thay đổi, và nếu chúng ta chỉ cài đặt chúng như những thuộc tính và lấy chúng thông qua getter thì hoàn toàn không cần dùng tới tính đa hình. Vì vậy, chúng ta có thể coi chúng như những phương thức trả về giá trị cố định.
Ở lớp vật chủ, trước hết chúng ta cần có mã vật chủ, và câu hỏi sẽ là những thuộc tính còn lại, sẽ là gì? Trước hết chúng ta quan sát thấy, triệu chứng là những gì chúng ta sẽ cài đặt trên lớp virus, vì vậy nên để xuất ra triệu chứng thì chúng ta ít nhất phải lưu lại danh sách những virus vật chủ đã bị nhiễm. Nhưng còn triệu chứng của virus tác động lên mỗi vật chủ thì không phải của virus mà là của riêng vật chủ, và mỗi loại virus lại có thể gây nên tác động khác nhau cho vật chủ. Vì vậy, chúng ta cũng cần lưu thêm danh sách triệu chứng ứng với mỗi loại virus.
Ngoài ra còn một yếu tố nữa, đó là tình trạng tử vong của vật chủ. Giả sử chúng ta chỉ xác định riêng biệt tình trạng tử vong của từng loại virus với vật chủ, thì có thể xảy ra trường hợp virus trước đã gây tử vong cho vật chủ, khi vật chủ bị nhiễm thêm 1 loại virus nữa, nhưng không gây tử vong, thì vật chủ đó đã sống lại ??? Vì vậy trong vật chủ sẽ có một thuộc tính để xác định là đã tử vong hay chưa.
Vấn đề cuối cùng, là đề bài có đề cập đến việc tiêm vaccine, và vaccine này sẽ ảnh hưởng tới tỉ lệ mắc triệu chứng. Với trường hợp chia khả năng miễn dịch ra như chúng ta đã nói, đã tiêm vaccine hay chưa sẽ trở thành một thuộc tính của khả năng miễn dịch.
Bước 3: Thiết kế phương thức
Như lúc nãy đã nói ở trên, lớp virus sẽ có phương thức để lấy tỉ lệ tử vong trung bình, và từng loại virus sẽ trả về kết quả khác nhau, nên đây sẽ là phương thức (thuần) ảo ở lớp virus.
Tương tự là lớp khả năng miễn dịch cũng sẽ có các phương thức trả về khả năng miễn dịch các mức độ triệu chứng.
Và cuối cùng, ở lớp vật chủ, chúng ta sẽ dựa theo các yêu cầu đề bài để thiết kế các phương thức thích hợp.
Sơ đồ thiết kế lớp
Lời giải tham khảo
Lưu ý: Ở tất cả vị trí cần gọi random, lưu lại kết quả, nếu không 2 lần gọi random sẽ cho ra kết quả khác nhau.
Bài làm khi thi trên giấy không bắt buộc phải chia file như bài làm tham khảo bên dưới. Các bạn có thể trình bày dưới dạng 1 file main.cpp duy nhất.
Virus.h
#pragma once
#define LOAI_VIRUS_SARS 0
#define LOAI_VIRUS_EBOLA 1
#define LOAI_VIRUS_HIV 2
class Virus
{
public:
virtual float LayXacSuatTuVongTrungBinh() = 0;
virtual void XuatTrieuChungNhe() = 0;
virtual void XuatTrieuChungNang() = 0;
virtual int LayLoaiVirus() = 0;
};
VirusSARS.h
#pragma once
#include "Virus.h"
#include <iostream>
class VirusSARS : public Virus
{
public:
float LayXacSuatTuVongTrungBinh() override { return 0.05f; }
void XuatTrieuChungNhe() override {
std::cout << "Cam cum thong thuong nhu sot, ho, mat vi giac trong vai ngay.\n";
}
void XuatTrieuChungNang() override {
std::cout << "Sot cao, ho khan, kho tho va doi luc kem theo dau dau du doi.\n";
}
int LayLoaiVirus() { return LOAI_VIRUS_SARS; }
};
VirusEbola.h
#pragma once
#include "Virus.h"
#include <iostream>
class VirusEbola : public Virus
{
public:
float LayXacSuatTuVongTrungBinh() override { return 0.5; }
void XuatTrieuChungNhe() override {
std::cout << "Sot, dau hong, dau co va dau dau.\n";
}
void XuatTrieuChungNang() override {
std::cout << "Non mua, tieu chay va nang nhat la xuat huyet ca ngoai lan ben trong.\n";
}
int LayLoaiVirus() { return LOAI_VIRUS_EBOLA; }
};
VirusHIV.h
#pragma once
#include "Virus.h"
#include <iostream>
class VirusHIV : public Virus
{
public:
float LayXacSuatTuVongTrungBinh() override { return 0.9f; }
void XuatTrieuChungNhe() override {
std::cout << "Cam sot.\n";
}
void XuatTrieuChungNang() override {
std::cout << "He mien dich da bi suy yeu dang ke, met moi cuc do khong giai thich duoc, sung hach keo dai, lo loet, viem phoi, tieu chay nang.\n";
}
int LayLoaiVirus() { return LOAI_VIRUS_HIV; }
};
KhaNangMienDich.h
#pragma once
#include <map>
class KhaNangMienDich
{
protected:
// Ở đây, mình dùng map để có thể dễ mở rộng cho trường hợp 4, 5 loại virus cho các bạn muốn mở rộng thêm.
// Theo scope của đề bài, các bạn chỉ cần dùng mảng 3 phần tử cũng được.
std::map<int, bool> daTiemVaccine; // Đã tiêm vaccine cho từng loại virus
public:
virtual float LayMienDichKhongTrieuChung(int loaiVirus) = 0;
virtual float LayMienDichTrieuChungNhe(int loaiVirus) = 0;
virtual float LayMienDichTrieuChungNang(int loaiVirus) = 0;
virtual float LayXacSuatTuVong(int loaiVirus) = 0;
void TiemVaccine(int loaiVirus) { daTiemVaccine[loaiVirus] = true; }
};
KhaNangMienDichThap.h
#pragma once
#include "KhaNangMienDich.h"
class KhaNangMienDichThap : public KhaNangMienDich
{
public:
float LayMienDichKhongTrieuChung(int loaiVirus) {
if (daTiemVaccine[loaiVirus])
return 0.1f;
return 0.05f;
}
float LayMienDichTrieuChungNhe(int loaiVirus) {
if (daTiemVaccine[loaiVirus])
return 0.4f;
return 0.15f;
}
float LayMienDichTrieuChungNang(int loaiVirus) {
if (daTiemVaccine[loaiVirus])
return 0.5f;
return 0.8f;
}
float LayXacSuatTuVong(int loaiVirus) {
if (daTiemVaccine[loaiVirus])
return 0.8f;
return 1.f;
}
};
KhaNangMienDichTrungBinh.h
#pragma once
#include "KhaNangMienDich.h"
class KhaNangMienDichTrungBinh : public KhaNangMienDich
{
public:
float LayMienDichKhongTrieuChung(int loaiVirus) {
if (daTiemVaccine[loaiVirus])
return 0.2f;
return 0.1f;
}
float LayMienDichTrieuChungNhe(int loaiVirus) {
if (daTiemVaccine[loaiVirus])
return 0.5f;
return 0.4f;
}
float LayMienDichTrieuChungNang(int loaiVirus) {
if (daTiemVaccine[loaiVirus])
return 0.3f;
return 0.5f;
}
float LayXacSuatTuVong(int loaiVirus) {
if (daTiemVaccine[loaiVirus])
return 0.6f;
return 0.7f;
}
};
KhaNangMienDichCao.h
#pragma once
#include "KhaNangMienDich.h"
class KhaNangMienDichCao : public KhaNangMienDich
{
public:
float LayMienDichKhongTrieuChung(int loaiVirus) {
if (daTiemVaccine[loaiVirus])
return 0.7f;
return 0.5f;
}
float LayMienDichTrieuChungNhe(int loaiVirus) {
if (daTiemVaccine[loaiVirus])
return 0.25f;
return 0.35f;
}
float LayMienDichTrieuChungNang(int loaiVirus) {
if (daTiemVaccine[loaiVirus])
return 0.5f;
return 0.15f;
}
float LayXacSuatTuVong(int loaiVirus) {
if (daTiemVaccine[loaiVirus])
return 0.4f;
return 0.5f;
}
};
VatChu.h
#pragma once
#include "KhaNangMienDich.h"
#include "Virus.h"
#include <vector>
#include <string>
#define TRIEUCHUNG_KHONGCO 0
#define TRIEUCHUNG_NHE 1
#define TRIEUCHUNG_NANG 2
class VatChu
{
private:
std::string maVatChu;
KhaNangMienDich* khaNangMienDich;
std::vector<Virus*> virusDaNhiem; // Danh sách các virus đã nhiễm
std::vector<int> trieuChung; // Triệu chứng của vật chủ ứng với từng loại virus
bool daTuVong;
private:
int Random(int a, int b); // Random 1 số x sao cho a <= x <= b
int XacDinhTrieuChung(Virus* virus);
bool XacDinhTuVong(Virus* virus);
public:
VatChu();
void Nhap();
void Xuat();
void ChoNhiemVirus(Virus* virus);
void TiemVaccine(int loaiVirus);
void XuatTrieuChung();
bool KiemTraTuVong();
bool KiemTraTrieuChungNang();
// Destructor không bắt buộc phải làm trong bài thi
~VatChu();
};
VatChu.cpp
#include "VatChu.h"
#include "KhaNangMienDichThap.h"
#include "KhaNangMienDichTrungBinh.h"
#include "KhaNangMienDichCao.h"
#include <iostream>
int VatChu::Random(int a, int b)
{
return rand() % (b - a + 1) + a;
}
int VatChu::XacDinhTrieuChung(Virus* virus)
{
float r = Random(0, 100) / 100.f;
float nguongMienDich = khaNangMienDich->LayMienDichKhongTrieuChung(virus->LayLoaiVirus());
if (r < nguongMienDich)
return TRIEUCHUNG_KHONGCO;
r -= nguongMienDich;
nguongMienDich = khaNangMienDich->LayMienDichTrieuChungNhe(virus->LayLoaiVirus());
if (r < nguongMienDich)
return TRIEUCHUNG_NHE;
return TRIEUCHUNG_NANG;
}
bool VatChu::XacDinhTuVong(Virus* virus)
{
float xsTuVong = khaNangMienDich->LayXacSuatTuVong(virus->LayLoaiVirus()) * virus->LayXacSuatTuVongTrungBinh();
return Random(0, 100) < xsTuVong * 100;
}
VatChu::VatChu()
{
maVatChu = "";
khaNangMienDich = nullptr;
daTuVong = false;
}
void VatChu::Nhap()
{
std::cout << "Nhap ma so: ";
std::cin >> maVatChu;
int mucDoMienDich;
std::cout << "Nhap muc do mien dich (0: thap, 1: trung binh, 2: cao): ";
std::cin >> mucDoMienDich;
switch (mucDoMienDich) {
case 0:
khaNangMienDich = new KhaNangMienDichThap();
break;
case 1:
khaNangMienDich = new KhaNangMienDichTrungBinh();
break;
case 2:
khaNangMienDich = new KhaNangMienDichCao();
break;
default:
break;
}
}
void VatChu::Xuat()
{
std::cout << "Ma vat chu: " << maVatChu << std::endl;
XuatTrieuChung();
if (daTuVong)
std::cout << "Vat chu da tu vong" << std::endl;
else
std::cout << "Vat chu chua tu vong" << std::endl;
}
void VatChu::ChoNhiemVirus(Virus* virus)
{
trieuChung.push_back(XacDinhTrieuChung(virus));
bool xdTuVong = XacDinhTuVong(virus);
if (xdTuVong)
daTuVong = xdTuVong; // Không gán trực tiếp daTuVong = xdTuVong mà không có if, vì bị tử vong mà tiêm 1 virus khác không gây tử vong thì cũng không "hồi sinh" lại được
virusDaNhiem.push_back(virus);
}
void VatChu::TiemVaccine(int loaiVirus) {
khaNangMienDich->TiemVaccine(loaiVirus);
}
void VatChu::XuatTrieuChung()
{
std::cout << "Trieu chung cua cac virus da nhiem la:" << std::endl;
for (int i = 0; i < virusDaNhiem.size(); ++i) {
if (virusDaNhiem[i]->LayLoaiVirus() == LOAI_VIRUS_SARS)
std::cout << "Trieu chung khi bi nhiem virus SARS-CoV-2 la: ";
else if (virusDaNhiem[i]->LayLoaiVirus() == LOAI_VIRUS_EBOLA)
std::cout << "Trieu chung khi bi nhiem virus Ebola la: ";
else if (virusDaNhiem[i]->LayLoaiVirus() == LOAI_VIRUS_HIV)
std::cout << "Trieu chung khi bi nhiem virus HIV la: ";
switch (trieuChung[i]) {
case TRIEUCHUNG_KHONGCO:
std::cout << "Khong co trieu chung" << std::endl;
break;
case TRIEUCHUNG_NHE:
virusDaNhiem[i]->XuatTrieuChungNhe();
break;
case TRIEUCHUNG_NANG:
virusDaNhiem[i]->XuatTrieuChungNang();
break;
default:
break;
}
}
}
bool VatChu::KiemTraTuVong()
{
return daTuVong;
}
bool VatChu::KiemTraTrieuChungNang()
{
for (int tc : trieuChung)
if (tc == TRIEUCHUNG_NANG)
return true;
return false;
}
VatChu::~VatChu()
{
delete khaNangMienDich;
for (auto& virus : virusDaNhiem)
delete virus;
}
main.cpp
#include <iostream>
#include "KhaNangMienDichThap.h"
#include "KhaNangMienDichTrungBinh.h"
#include "KhaNangMienDichCao.h"
#include "VirusSARS.h"
#include "VirusEbola.h"
#include "VirusHIV.h"
#include "VatChu.h"
using namespace std;
int main()
{
srand((unsigned int)time(0));
int n;
cout << "Nhap so luong vat chu: ";
cin >> n;
vector<VatChu*> dsVatChu;
int countTuVong = 0;
for (int i = 0; i < n; ++i) {
cout << "Nhap vat chu thu " << i + 1 << ":" << endl;
VatChu* vc = new VatChu();
vc->Nhap();
vc->ChoNhiemVirus(new VirusSARS());
vc->ChoNhiemVirus(new VirusEbola());
vc->ChoNhiemVirus(new VirusHIV());
dsVatChu.push_back(vc);
}
for (int i = 0; i < dsVatChu.size(); ++i)
dsVatChu[i]->Xuat();
cout << endl;
cout << "Nhap M = " << n << " vat chu moi:" << endl;
dsVatChu.clear();
for (int i = 0; i < n; ++i) {
cout << "Nhap vat chu thu " << i + 1 << ":" << endl;
VatChu* vc = new VatChu();
vc->Nhap();
vc->TiemVaccine(LOAI_VIRUS_SARS);
vc->TiemVaccine(LOAI_VIRUS_EBOLA);
vc->TiemVaccine(LOAI_VIRUS_HIV);
vc->ChoNhiemVirus(new VirusSARS());
vc->ChoNhiemVirus(new VirusEbola());
vc->ChoNhiemVirus(new VirusHIV());
dsVatChu.push_back(vc);
}
int soLuongTrieuChungNang = 0;
int soLuongTuVong = 0;
for (int i = 0; i < dsVatChu.size(); ++i)
soLuongTrieuChungNang += dsVatChu[i]->KiemTraTrieuChungNang(),
soLuongTuVong += dsVatChu[i]->KiemTraTuVong();
cout << "So luong vat chu gap trieu chung nang: " << soLuongTrieuChungNang << endl;
cout << "So luong vat chu tu vong: " << soLuongTuVong << endl;
}