You are on page 1of 21

TRƯỜNG ĐẠI HỌC BÁCH KHOA

KHOA CÔNG NGHỆ THÔNG TIN

ĐỒ ÁN LẬP TRÌNH TÍNH TOÁN

TÊN ĐỀ TÀI

Đề C05: Bạn muốn hẹn hò?


Người hướng dẫn: ThS. NGUYỄN THỊ LỆ QUYÊN
Sinh viên thực hiện:
Họ và tên:
Nguyễn Lê Việt Hoàng
LỚP: 21TCLC-DT1
Họ và tên: Nguyễn Anh Nguyên
LỚP: 21TCLC-DT1
NHÓM: 09

Đà Nẵng, 07/2022
MỤC LỤC
MỤC LỤC i
DANH MỤC HÌNH VẼ ii
MỞ ĐẦU i
1. TỔNG QUAN ĐỀ TÀI 1
2. CƠ SỞ LÝ THUYẾT 1
2.1. Ý tưởng 1
2.2. Cơ sở lý thuyết 1
3. TỔ CHỨC CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN 1
3.1. Phát biểu bài toán 1
3.2. Cấu trúc dữ liệu 1
3.3. Thuật toán 1
4. CHƯƠNG TRÌNH VÀ KẾT QUẢ 1
4.1. Tổ chức chương trình 1
4.2. Ngôn ngữ cài đặt 1
4.3. Kết quả 1
4.3.1. Giao diện chính của chương trình 1
4.3.2. Kết quả thực thi của chương trình 1
4.3.3. Nhận xét đánh giá 1
5. KẾT LUẬN VÀ HƯỚNG PHÁT TRIỂN 1
5.1. Kết luận 1
5.2. Hướng phát triển 1
TÀI LIỆU THAM KHẢO 2

1
DANH MỤC HÌNH VẼ

2
Đồ án lập trình tính toán

MỞ ĐẦU

Trong phần này, cần trình bày về: Mục đích thực hiện đề tài, mục tiêu đề tài,
phạm vi và đối tượng nghiên cứu, phương pháp nghiên cứu, cấu trúc của đồ môn học}
Mục đích thực hiện đề tài: Thông qua quá trình thực hiện đề tài có thêm kiến thức,
cũng
như áp dụng được những kiến thức đã học để giải quyết vấn đề, bài toán một cách
trơn
tru tối ưu nhất có thể.
Mục tiêu đề tài: giải quyết được bài toán được đưa ra.
Phạm vi và đối tượng nghiên cứu: sử dụng bao quát những kiến thức đã học và tập
trung
chủ yếu tại các cấu trúc trong cấu trúc dữ liệu, đối tượng nghiên cứu chính là cấu
trúc
dữ liệu tập hợp rời rạc và ứng dụng của nó.
Phương pháp nghiên cứu: sử dụng các tài liệu được phổ cập trên trường cũng như tìm
kiếm tài liệu từ Internet, đọc hiểu về cấu trúc dữ liệu tập hợp rời rạc, suy ra ý
tưởng áp
dụng kiến thức và hoàn thành đề bài.
Cấu trúc của đồ môn học:sau khi tìm hiểu các lý thuyết về về thuật toán Disjoint-
set và
các lý thuyết liên qua lên ý tưởng và giải quyết vấn đề được đưa ra, kiểm tra và
nâng
cấp thuật toán.

1
Đồ án lập trình tính toán

1. TỔNG QUAN ĐỀ TÀI


Chương trình Bạn muốn hẹn hò đã trở nên quá phổ biến trên truyền hình, và ở
bách khoa cũng đã có chương trình tương tự như vậy với quy mô nhỏ hơn. Trong đợt
này có bạn nam và bạn nữ đăng ký tham gia. Ban tổ chức có thông tin của tất cả
người
tham gia, kể cả trang cá nhân Facebook nên có thể biết được hai sinh viên có phải
là bạn
của nhau trên Facebook hay không. Và ban tổ chức không muốn ghép cặp hai sinh viên
ở trong cùng một nhóm bạn với nhau. Cho mối quan hệ bạn bè giữa các bạn sinh viên
nam với nhau, mối quan hệ bạn bè giữa các bạn nữ với nhau và mối quan hệ bạn bè
giữa
nam và nữ. Ban tổ chức muốn tính xem tổng cộng có bao nhiêu cặp sinh viên nam nữ
có thể ghép cặp được với nhau.

2. CƠ SỞ LÝ THUYẾT
2.1. Ý tưởng
Phân chia các sinh viên thành các nhóm khác nhau theo các nhóm mà sinh viên
thuộc về. Phương pháp này được gọi là cấu trúc dữ liệu tập hợp phân biệt duy trì
tập
hợp các tập hợp rời rạc và mỗi tập hợp được đại diện bởi đại diện của nó là một
trong
các thành viên của nó.
2.2. Cơ sở lý thuyết
2.2.1. Disjoint-set data structure
Cấu trúc dữ liệu tập hợp rời rạc , còn được gọi là cấu trúc dữ liệu liên
hợp-
tìm hoặc hợp nhất tìm tập hợp , là một cấu trúc dữ liệu lưu trữ một tập hợp các tập
hợp
rời rạc (không chồng chéo). Tương tự, nó lưu trữ một phân vùng của một tập hợp
thành
các tập con rời rạc. Nó cung cấp các hoạt động để thêm các tập hợp mới, hợp nhất
các
tập hợp (thay thế chúng bằng liên hiệp của chúng ) và tìm thành viên đại diện của
một
tập hợp.
2.2.2. Union (set theory)
Trong lý thuyết tập hợp, hợp (ký hiệu là ∪) của một tập hợp các tập hợp là
tập
hợp của tất cả các phần tử trong tập hợp. [1] Nó là một trong những phép toán cơ
bản
mà qua đó các tập hợp có thể được kết hợp và liên quan với nhau. Liên hợp nullary
đề
cập đến liên hợp của các tập hợp 0 và theo định nghĩa, nó tương đương với tập hợp
rỗng.
2.2.3. Forest
Rừng là một đồ thị vô hướng trong đó hai đỉnh bất kỳ được nối với nhau bằng
nhiều nhất một đường đi. Tương tự, rừng là một đồ thị xoay chiều vô hướng, tất cả
các

1
Đồ án lập trình tính toán

thành phần được kết nối của chúng là cây; nói cách khác, biểu đồ bao gồm sự kết hợp
rời rạc của các cây. Trong những trường hợp đặc biệt, biểu đồ bậc không (một khu
rừng
không có cây nào), một cây đơn lẻ và một biểu đồ không viền, là những ví dụ về
rừng.
Vì với mọi cây V - E = 1, chúng ta có thể dễ dàng đếm số cây trong một khu rừng
bằng
cách trừ đi hiệu số giữa tổng số đỉnh và tổng số cạnh. TV - TE = số cây trong rừng.

3. TỔ CHỨC CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN


3.1. Phát biểu bài toán
Mô tả đầu vào (Input) và đầu ra (Output)
Input:
- Dòng đầu là số 2 nguyên là số lượng sinh viên nam và số lượng sinh viên
nữ tham gia chương trình
- Dòng tiếp theo chứa số a, theo sau là a dòng, mỗi dòng gồm 2 số và thể hiện
bạn nam và bạn nam là bạn của nhau trên Facebook
- Dòng tiếp theo chứa số b, theo sau là b dòng, mỗi dòng gồm 2 số và thể hiện
bạn nữ và bạn nữ là bạn của nhau trên Facebook
- Dòng tiếp theo chứa số c, theo sau là c dòng, mỗi dòng gồm 2 số và thể hiện
bạn nam và bạn nữ là bạn của nhau trên Facebook
Output:
- In kết quả số cặp sinh viên nam nữ có thể ghép cặp được với nhau.
3.2. Cấu trúc dữ liệu
Canh lề phải
thẳng hàng
Mảng: Một mảng các số nguyên, được gọi là cha []. Nếu chúng ta đang xử lý n
mục, phần tử thứ i của mảng đại diện cho mặt hàng thứ i. Chính xác hơn, phần
tử thứ i của mảng là phần tử cha của mục thứ i. Những mối quan hệ này tạo ra
một hoặc nhiều cây ảo.
Cây: Nó là một tập hợp rời rạc. Nếu hai phần tử nằm trong cùng một cây, thì
chúng nằm trong cùng một tập rời rạc. Nút gốc (hay nút trên cùng) của mỗi cây
được gọi là đại diện của tập hợp. Luôn luôn có một đại diện duy nhất duy nhất
của mỗi bộ . Một quy tắc đơn giản để xác định đại diện là, nếu tôi là đại
diện
của một tập hợp, thì cha [i] = i. Nếu tôi không phải là đại diện của tập hợp
của
anh ấy, thì có thể tìm thấy nó bằng cách đi lên cây cho đến khi chúng tôi tìm
thấy đại diện.

2
Đồ án lập trình tính toán

3.3. Thuật toán Code đã có ở phần phụ lục, ko dán code ở đây
Thay thế bằng sơ đồ khối hoặc giả code
Trình bày các thuật toán và phân tích độ phức tạp của các thuật toán.

-Hàm find(): trả về vị trí người đại diện;

Canh
lề phải thẳng hàng

-Hàm Union: Trước hết, chúng ta cần một mảng số nguyên mới được gọi là xếp hạng
[]. Kích thước của mảng này giống như mảng mẹ. Nếu i là đại diện của một tập hợp,
thì rank[i] là chiều cao của cây đại diện cho tập hợp đó. Bây giờ những gì chúng ta
muốn làm là giảm thiểu chiều cao của cây kết quả. Nếu chúng ta đang hợp nhất hai
cây

3
Đồ án lập trình tính toán

(hoặc tập hợp), hãy gọi chúng là trái và phải, sau đó tất cả phụ thuộc vào thứ hạng
của
bên trái và thứ hạng của bên phải.
● Nếu thứ hạng bên trái nhỏ hơn thứ hạng bên phải, thì tốt nhất bạn nên di
chuyển
sang trái dưới bên phải, vì điều đó sẽ không thay đổi thứ hạng bên phải (trong
khi di chuyển sang phải dưới bên trái sẽ làm tăng chiều cao). Theo cách tương
tự, nếu thứ hạng của bên phải nhỏ hơn thứ hạng của bên trái, thì chúng ta nên
di
chuyển sang phải dưới bên trái.

● Nếu các thứ hạng bằng nhau, không quan trọng cây nào đi dưới cây kia, nhưng
thứ hạng của kết quả sẽ luôn lớn hơn thứ hạng của các cây.
-Độ phức tạp thuật toán: Độ cao tối đa của cây là log(n)
Do vậy độ phức tạp của mỗi tạo tác find - union là O( log(n) )

4
Đồ án lập trình tính toán

4. CHƯƠNG TRÌNH VÀ KẾT QUẢ


4.1. Tổ chức chương trình
Định dạng văn bản ,
canh lề thống nhất Gồm hàm Input để nhập dữ liệu , hàm solve để xử lý bài
toán , hàm
toàn bộ văn bản
Union để hợp nhất 2 tập hợp , hàm find để tìm người đại
diện , hàm main để
thực thi các hàm trên
4.2. Ngôn ngữ cài đặt
Ngôn ngữ lập trình C : là một ngôn ngữ mệnh lệnh được phát
triển từ đầu thập
niên 1970 bởi Dennis Ritchie để dùng trong hệ điều hành UNIX. Từ
đó, ngôn ngữ này
đã lan rộng ra nhiều hệ điều hành khác và trở thành một những ngôn
ngữ phổ dụng
nhất. C là ngôn ngữ rất có hiệu quả và được ưa chuộng nhất để viết
các phần mềm hệ
thống, mặc dù nó cũng được dùng cho việc viết các ứng dụng. Ngoài
ra, C cũng
thường được dùng làm phương tiện giảng dạy trong khoa học máy tính
mặc dù ngôn
ngữ này không được thiết kế dành cho người nhập môn.
4.3. Kết quả
4.3.1. Giao diện chính của chương trình Cần thêm hình demo, 3-
4 hình gì đó

Thiếu chú thích hình

5
Đồ án lập trình tính toán

4.3.2. Kết quả thực thi của chương trình


Mô tả kết quả thực hiện chương trình.
Ở đây chúng ta sử dụng 1 thuật toán kém hiệu quả hơn để kiểm tra tính chính xác và
thời gian chạy của thuật toán Disjoint Set

test case 1 test case 2 test case 3


Thuật Disjoint Set 0.01s 0.01s 0.01s
Thuật tham lam 0.01s 0.15s 0.03s

4.3.3. Nhận xét đánh giá


Gọi n = Nam + Nữ
- Khi số lượng n bé, 2 thuật toán có thời gian chạy tương đương nhau ; nhưng
khi
số lượng n tăng lên, thuật Disjoint Set có thời gian chạy nhanh hơn hẳn
=> thuật Disjoint Set có thời gian chạy tối ưu hơn, thích hợp để tính toán với các
số
lớn ( n khoảng 10^6 thì thuật Disjoint Set chạy khoảng dưới 1s )
- Thuật tham lam dùng mảng 2 chiều nên không lưu được các giá trị nếu n tương
đối lớn ( n> 5000 ) , trong khi đó thuật Disjoint Set chỉ dùng mảng 1 chiều
để
lưu kết quả nên có thể chứa các giá trị với n rất lớn ( n khoảng 10^6 )
=> thuật Disjoint Set sử dụng bộ nhớ tối ưu hơn và chứa được n tới 10^6 và có thể
nâng số phần tử của mảng lên để chứa n tới 10^7

5. KẾT LUẬN VÀ HƯỚNG PHÁT TRIỂN


5.1. Kết luận
Vậy code áp dụng thuật toán Disjoint set được phát triển tương đối tối ưu
Độ phức tạp về thời gian O(nlog(n))
Độ phức tạp về bộ nhớ O(3*10^6)
Với n<=10^6 code sẽ chạy dưới 1s, đủ để áp dụng cho các bài tập trên lớp
hoặc
các tình huống thực tế

6
Đồ án lập trình tính toán

5.2. Hướng phát triển


- Thiết kế giao diện đẹp hơn
- Chuyển sang ngôn ngữ lập trình bậc cao hơn ( như C++ ) để sử dụng các cấu
trúc dữ liệu trong thư viện để tối ưu hóa chương trình. Vd : dùng cấu trúc
dữ
liệu map thay cho mảng groupsize để giảm bộ nhớ cần sử dụng

7
Đồ án lập trình tính toán

TÀI LIỆU THAM KHẢO


[1] Disjoint Set Data Structures : https://www.geeksforgeeks.org/disjoint-set-data-
structures/

[2] Độ Phức Tạp Của Thuật Toán Và Lựa Chọn Cách Giải Thuật :
https://codelearn.io/sharing/do-phuc-tap-cua-thuat-toan-va-lua-chon-cach-giai-
thuat#:~:text=Tuy%20
nhi%C3%AAn%20v%E1%BB%9Bi%201%20s%E1%BB%91%20gi%E1%BA%A3i
%20thu%E1%BA%ADt%20s%E1%BA%AFp,ch%E1%BA%A1y%20c%C3%B3%20
th%E1%BB%9Di%20gian%20x%E1%BA%A5p%20x%E1%BB%89%20b%E1%BA
%B1ng%20nhau.

[3] Zvi Galil and Giuseppe F. Italiano : Data structures and algorithms for
disjoint set
union problems : https://dl.acm.org/doi/abs/10.1145/116873.116878

[4] Ashwin Pananjady : The online disjoint set cover problem and its applications
https://ieeexplore.ieee.org/abstract/document/7218497

8
Đồ án lập trình tính toán

PHỤ LỤC
Sinh viên bỏ Code từng phần vào đây.
Code dùng thuật Disjoint-set :
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define ll long long
int Nam , Nu , a, b , c ;
int Parent[1000009] , Rank[1000009] ;
int find(int i) {
if (Parent[i] == i) return i;
else{
int result = find(Parent[i]);

Parent[i] = result;

return result;
}
}
void Union(int i, int j) {

int irep = find(i);

int jrep = find(j);

if (irep == jrep) return;

int irank = Rank[irep] ;


9
Đồ án lập trình tính toán

int jrank = Rank[jrep];

if (irank < jrank){


Parent[irep] = jrep;
}

else if (jrank < irank) {


Parent[jrep] = irep;
}

else{

Parent[irep] = jrep;

Rank[jrep]++;
}
}
void Input(){

printf("Nhap so luong nam va so luong nu : ") ;


scanf("%d%d",&Nam,&Nu) ;
int i ,tem1 , tem2 ;
// Buoc khoi tao
for(i=0;i<=Nam+Nu;i++) {
Rank[i]=0;
Parent[i]=i;
}

printf("Nhap so luong cac cap ban be giua cac ban nam : ") ;

10
Đồ án lập trình tính toán

scanf("%d",&a) ;
printf("Nhap moi quan he ban be giua cac ban nam : \n") ;
for (i=0;i<a;i++) {
scanf("%d%d",&tem1,&tem2) ;
Union(tem1,tem2) ;
}

printf("Nhap so luong cac cap ban be giua cac ban nu : ") ;


scanf("%d",&b) ;
printf("Nhap moi quan he ban be giua cac ban nu : \n") ;
for (i=0;i<b;i++) {
scanf("%d%d",&tem1,&tem2) ;
Union(tem1+Nam,tem2+Nam) ;
}

printf("Nhap so luong cac cap ban be giua cac ban nam va cac ban nu : ") ;
scanf("%d",&c ) ;
printf("Nhap moi quan he ban be giua cac ban nam va cac ban nu : \n") ;
for (i=0;i<c;i++) {
scanf("%d%d",&tem1,&tem2) ;
Union(tem1,tem2+Nam) ;
}

}
int groupsize[1000009] ;
void solve(){
int i,j ;

for(i=0;i<=Nam+Nu;i++) groupsize[i] = 0 ;

11
Đồ án lập trình tính toán

for(i=Nam+1;i<=Nam+Nu;i++){
int father = find(i) ;
groupsize[father]++ ;
}
ll res =0 ;
for(i=1;i<=Nam;i++){
res+= Nu-groupsize[find(i)] ;
}

printf("\n\n
_________________________________________________\n\n") ;
printf("So cap sinh vien nam nu co the ghep cap voi nhau : %lld",res) ;
printf("\n\nBan co muon liet ke ra so cap nam nu co the ghep cap voi
nhau khong
? \n") ;
printf("Co : 1 \n") ;
printf("khong : 0 \n") ;
printf("Nhap lua chon : ") ;
int choose = 1 ;
scanf("%d",&choose) ;
if (choose){
printf("\nKet qua :\n") ;
int i , j ;
for (i=1;i<=Nam;i++){
for(j=1;j<=Nu;j++){
if ( find(i)!=find(j+Nam) ){
printf("Nam : %d va Nu : %d \n",i,j);
}
}
}
}

12
Đồ án lập trình tính toán

int main(){
system("COLOR F4");

printf(" -------------------- Program C05 --------------- \n") ;


printf(" --------- CHUONG TRINH BAN MUON HEN HO ---------
\n\n") ;

int chon1 , chon2 ;


printf("Chon cach nhap vao du lieu : \n") ;
printf("Nhap vao truc tiep : 1 \n") ;
printf("Nhap vao tu file : 0 \n") ;
scanf("%d",&chon1) ;

printf("Chon cach in ra du lieu : \n") ;


printf("In ra truc tiep : 1 \n") ;
printf("In ra file : 0 \n") ;
scanf("%d",&chon2) ;
if (chon1 == 0) {
freopen( "input.txt", "r", stdin);
}
if (chon2 ==0 ){
freopen( "output.txt", "w", stdout);
}

int luachon = 0 ;
do {

Input() ; solve() ;

13
Đồ án lập trình tính toán

printf("\n\n\n\n\n
==================================================") ;
printf("\n\nBan co muon tiep tuc khong ?\n") ;
printf("Co : 1 \n") ;
printf("khong : 0 \n") ;
printf("Nhap lua chon : ") ;
scanf("%d",&luachon) ;
if(luachon) printf("\n\n\n") ;

}
while(luachon) ;
}

Code dùng thuật tham lam :

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define ll long long

int Nam , Nu , a, b , c ;
int Path[5000][5000] ;
int check[5000] , visited[5000] ;
void DFS(int u) {
if (visited[u]) return ;

14
Đồ án lập trình tính toán

visited[u] = 1 ;
int i ;
for(i=1;i<=Nam+Nu;i++){
if (Path[u][i]){
check[i] = 1 ;
DFS(i) ;
}
}
}
void Input(){
scanf("%d%d",&Nam,&Nu) ;
int i ,tem1 , tem2 ;

scanf("%d",&a) ;
for (i=0;i<a;i++) {
scanf("%d%d",&tem1,&tem2) ;
Path[tem1][tem2] = Path[tem2][tem1] = 1 ;
}

scanf("%d",&b) ;
for (i=0;i<b;i++) {
scanf("%d%d",&tem1,&tem2) ;
Path[tem1+Nam][tem2+Nam] = Path[tem2+Nam][tem1+Nam] = 1 ;
}

scanf("%d",&c ) ;
for (i=0;i<c;i++) {
scanf("%d%d",&tem1,&tem2) ;
Path[tem1][tem2+Nam] = Path[tem2+Nam][tem1] = 1 ;
}

15
Đồ án lập trình tính toán

}
void solve(){
int i , j , res = 0 ;
for(i=1;i<=Nam;i++){
for(j=1;j<=Nam+Nu;j++) check[j] =
visited[j] = 0 ;
DFS(i) ;
for(j=Nam+1;j<=Nam+Nu;j++){
if(check[j]==0) res++;
}
}
printf("result : %lld",res) ;
}

int main(){

Input() ; solve() ;
}

16

You might also like