Tải bản đầy đủ (.doc) (59 trang)

Một số thuật toán liên quan đến nội dung hình học

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (265 KB, 59 trang )

ĐẠI HỌC THÁI NGUYÊN
NGUYỄN THANH HOA
MỘT SỐ THUẬT TOÁN LIÊN QUAN ĐẾN
NỘI DUNG HÌNH HỌC
Chuyên ngành: Tin học
LUẬN VĂN TỐT NGHIỆP ĐẠI HỌC

Người hướng dẫn khoa học:
Thạc sĩ: Nguyễn Văn An
THÁI NGUYÊN - 2010
2
MỤC LỤC
Trang
Trang bìa phụ 1
Mục lục 2
Mở đầu 4
1. Lý do chọn đề tài 4
2. Mục đích - yêu cầu và phạm vi nghiên cứu 5
3. Phương pháp nghiên cứu 5
4. Cấu trúc của luận văn 5
Chương 1. Cơ sở lý thuyết 7
1.1. Một số đối tượng hình học 7
1.1.1. Điểm 7
1.1.2. Đường thẳng 7
1.1.3. Đoạn thẳng 8
1.1.4. Đa giác 8
1.2. Phương trình tương quan giữa điểm và đường thẳng, đoạn thẳng 9
1.2.1. Tương quan giữa điểm và đường thẳng 9
1.2.2. Tương quan giữa điểm và đoạn thẳng 10
1.3. Giao giữa đường thẳng, đoạn thẳng 10
1.3.1. Đường thẳng cắt đường thẳng 10


1.3.2. Đường thẳng cắt đoạn thẳng 11
1.3.3. Đoạn thẳng cắt đoạn thẳng 11
1.4. Một số bài tập áp dụng 13
1.4.1. Kiểm tra một điểm thuộc một tia 13
1.4.2. Kiểm tra một tia cắt một đoạn 13
1.4.3. Kiểm tra tính lồi, lõm của đa giác 14
1.4.4. Kiểm tra vị trí của một điểm với một đa giác 17
3
1.4.5. Bài toán tìm phần giao, phần hợp của hai đường tròn 19
1.4.6. Bài toán áp dụng phương pháp lưới 23
1.4.7. Tìm tâm và tỉ số vị tự biến đường tròn này thành đường tròn kia
27
Chương 2. Thuật toán tìm phần giao và phần hợp của hai đa giác 29
2.1. Bài toán 1 29
2.1.1. Thuật toán tìm phần giao của hai đa giác 31
2.1.2. Tóm tắt thuật toán 32
2.1.3. Mô tả một số hàm, thủ tục trong chương trình 33
2.1.4. Đánh giá thuật toán 34
2.1.5. Thuật toán tìm phần hợp của hai đa giác 35
2.1.6. Minh họa một số test 35
2.2. Bài toán 2 37
2.2.1. Thuật toán 39
2.2.2. Một số bộ test 40
2.3. Bài toán 3 41
Chương 3. Thuật toán tính thể tích nước ngập trong mô hình dữ liệu
địa lý Raster 42
3.1. Đặt vấn đề 42
3.2. Mô hình dữ liệu Raster 43
3.3. Thuật toán 43
3.4. Mô phỏng chương trình minh họa 46

3.5. Một số bộ test 46
3.6. Đánh giá thuật toán 50
3.7. Mở rộng 50
Kết luận 51
Tài liệu tham khảo 52
Phụ lục 53

4
MỞ ĐẦU
1. Lý do chọn đề tài
Như chúng ta đã biết Toán học có ảnh hưởng rất lớn đến mọi lĩnh vực
của cuộc sống. Các bài toán trong tin học nếu có thuật toán giải dựa trên cơ sở
lý thuyết toán học vững chắc thì sẽ đem lại kết quả tốt. Điều đó được khẳng
định rõ trong các bài toán liên quan đến nội dung về hình học. Những bài toán
dạng này đòi hỏi người lập trình không những phải nắm vững các kiến thức
về toán học mà còn phải có tư duy thuật toán tốt.
Đa số các thuật toán đều tập trung vào dữ liệu kiểu văn bản và kiểu số,
chúng được thiết kế và xử lý sẵn trong phần lớn các môi trường lập trình. Đối
với các bài toán hình học thì tình huống khác hẳn, ngay cả các phép toán sơ
cấp trên các đối tượng là điểm và đoạn thẳng cũng có thể là một thách thức về
tính toán và lập trình. Những bài toán hình học thường trực quan và cũng
không quá khó về mặt giải thuật nhưng lại rườm rà về mặt chương trình.
Chính vì thế mà chỉ cần một sai sót nhỏ cũng dẫn đến việc khó có thể chỉnh
sửa chương trình trong thời gian cho phép. Bên cạnh đó, trên thực tế nhiều bài
toán hình học có thể giải quyết được ngay lập tức bằng cách nhìn vào hình vẽ
nhưng lại đòi hỏi những chương trình không đơn giản, ví dụ như kiểm tra một
điểm có nằm trong đa giác hay không?. Có thể vì những lý do như trên mà
nhiều nguời còn e ngại khi lập trình các bài toán liên quan đến nội dung hình
học mặc dù họ không hề phủ nhận đó là những bài toán rất hay.
Xuất phát từ nhận thức trên, được sự đồng ý của Hội đồng khoa học

Khoa CNTT và sự hướng dẫn của Thầy giáo Nguyễn Văn An, em đã lựa chọn
đề tài “Một số thuật toán liên quan đến nội dung hình học” làm luận văn
tốt nghiệp của mình.
5
2. Mục đích - yêu cầu và phạm vi nghiên cứu
* Tìm hiểu và lập trình thể hiện một số thuật toán liên quan đến nội dung
hình học.
* Áp dụng vào một số bài toán cụ thể như: Tìm thể tích nước ngập trong
một vùng, tìm phần giao của các đa giác, một số đề thi học sinh giỏi, đề thi
Olimpic
3. Phương pháp nghiên cứu
* Nghiên cứu tài liệu, sách báo
* Lấy ý kiến chuyên gia.
* Lập trình minh họa các thuật toán liên quan.
4. Cấu trúc của luận văn
Luận văn bao gồm 3 chương và phần phụ lục:
Chương 1: Trình bày các khái niệm về các đối tượng hình học cơ bản,
mối tương quan giữa chúng và một số bài tập áp dụng.
Chương 2: Trình bày chi tiết thuật toán tìm phần giao và phần hợp của
hai đa giác và bài toán mở rộng của thuật toán này.
Chương 3: Trình bày thuật toán tìm thể tích nước ngập trong mô hình
dữ liệu địa lý Raster.
Phần phụ lục là cài đặt minh họa một số hàm, thủ tục liên quan đến
thuật toán tìm phần giao của hai đa giác.
Để hoàn thành luận văn này, cùng với sự nỗ lực của bản thân là sự giúp
đỡ chỉ bảo tận tình, cặn kẽ của của Thầy giáo Nguyễn Văn Trường. Em xin
chân thành cảm ơn sự giúp đỡ chỉ bảo quý báu của Thầy!
Em xin chân thành cảm ơn sự tạo điều kiện của Ban chủ nhiệm khoa
Toán, sự động viên, chỉ bảo của các Thầy Cô giáo trong Khoa đã giúp em
hoàn thành khóa luận này!

6
Tôi xin cảm ơn các bạn sinh viên lớp sư phạm Tin Học K37 đã cổ vũ
động viên tôi trong quá trình thực hiện khóa luận!
Dù đã có nhiều cố gắng nhưng luận văn không tránh khỏi những thiếu
sót, tác giả rất mong nhận được sự đóng góp ý kiến của các Thầy, Cô và các
bạn để khóa luận được hoàn thiện.
7
Chương 1. CƠ SỞ LÝ THUYẾT
Chương này giới thiệu những cơ sở lý thuyết cơ bản nhất về các đối
tượng hình học, qua đó giúp đọc giả có thể hiểu rõ hơn về các đối tượng hình
học và mối tương quan giữa chúng.
1.1. Một số đối tượng hình học
1.1.1. Điểm
Điểm thường được xét bằng một cặp số thực là tọa độ của điểm đó
trong hệ trục tọa độ Descarter thường dùng [4].
Trong hầu hết các bài toán hình học chúng ta sẽ dùng kiểu lưu trữ đơn
giản, dễ hiểu như sau:
typedef struct
{
float x, y;
}xy;
Chính vì vậy khi xét tới tọa độ của P(x, y) thì ta xét tới P.x, P.y.
Chúng ta có thể tính được khoảng cách giữa hai điểm A(x1, y2), B(x2,
y2) trong mặt phẳng tọa độ như sau:
float khoangcach (xy A, xy B)
{float T;
T = sqrt ((A.x - B.x)* (A.x- B.x) +(A.y - B.y)*(A.y- B.y));
return T;
}
1.1.2. Đường thẳng

Trong hình học, chúng ta có phương trình của một đường thẳng trong
mặt phẳng: Ax + By + C = 0. Chúng ta coi A, B, C là biểu diễn cho đường
8
thẳng đó. Nếu một đường thẳng (d): Ax + By + C = 0, đi qua 2 điểm A(x1,
y1) và B(x2, y2) thì ta có:
A: = y1- y2;
B: = x2 - x1;
C: = -(A*x1 + B* y1); //[8]
1.1.3. Đoạn thẳng
Đoạn thẳng là một cặp điểm được nối với nhau bởi một phần của
đường thẳng [4].
1.1.4. Đa giác
1.1.4.1. Tam giác
Tam giác được định nghĩa là tập ba điểm không thẳng hàng: A(x1, y1),
B(x2, y2), C(x3, y3). Chúng ta có thể tính diện tích tam giác theo công thức
Herong S = Sqrt ((p - a)*(p - b)*(p - c)*p), trong đó a, b, c là độ dài ba cạnh
của tam giác, p = (a + b + c)/2 hoặc tính theo công thức:
1
,
2
S AB AC
 
 
=
r
r
[4].
1.1.4.2. Hình chữ nhật
Chúng ta xét trường hợp cho các cạnh song song với các trục tọa độ.
Trong hệ tọa độ Descarter vuông góc, xét một hình chữ nhật ABCD, A(x1,

y1); B(x2, y2); C(x3, y3); D(x4, y4). Nhưng có một điều đặc biệt là chúng ta
chỉ cần xác định tọa độ đỉnh của hai đỉnh đối nhau thì xác định được một hình
chữ nhật duy nhất. Chính vì thế thông thường chúng ta gọi tọa độ của điểm
dưới trái và điểm trên phải là hai điểm đặc trưng cho hình chữ nhật đó [4].
1.1.4.3. Đa giác
Đa giác là một dãy các điểm với hai điểm liên tiếp được nối bởi một
đoạn thẳng, và điểm đầu nối với điểm cuối tạo thành một hình gấp khúc khép
kín.
9
Đa giác được gọi là đa giác lồi khi mọi điểm còn lại của đa giác nằm
cùng phía với nhau so với một cạnh nào đó [4].
Để tính diện tích đa giác (lồi hoặc lõm và không tự cắt) gồm n đỉnh A[1],
A[2], , A[n] ta làm như sau:
Chia đa giác thành n - 2 tam giác rồi tính tổng diện tích của các tam
giác ấy. Tuy nhiên phương pháp này dài dòng, ta làm cách khác như sau:
Chia đa giác thành các hình thang bằng cách chiếu các cạnh xuống trục
hoành (hình vẽ). Hình thang được xác lập bởi cạnh A[i]A[i+1] có diện tích là
Abs (S) với:
Sau khi gán đỉnh A[n+1] = A[1] ta tính diện tích
toàn phần của đa giác như sau:
S = 0;
For (i: = 1; i< = n; i++)
S = (1/2) * Abs (S);
Chú ý:
a. Hoàn toàn tương tự ta có thể tìm diện tích của đa giác bằng cách
chiếu các cạnh xuống trục tung.
b. Nếu thay bước gán S = (1/2) * abs (S) bởi S = S/2 thì dấu của S là
dương hay âm sẽ cho ta biết chiều đánh số của các đỉnh theo thứ tự từ 1, 2, ,
n là ngược hay xuôi chiều kim đồng hồ.
c. Thông thường ta dùng mảng một chiều để biểu diễn một đa giác.

1.2. Phương trình tương quan giữa điểm và đường thẳng, đoạn thẳng
1.2.1. Tương quan giữa điểm và đường thẳng
[ ] [ ]
( )
[ ] [ ]
( )
1
. . 1 . * . 1 .
2
S A i x A i x A i y A i y
= − + + +
[ ] [ ]
( )
[ ] [ ]
( )
. 1 . * . 1 . ;S S A i x A i x A i y A i y
= + − + + +
y
x
A
i
+
+i
A
i+1
O
10
Phương trình đường thẳng đi qua hai điểm phân biệt A(x
a
, y

a
), B(x
b
, y
b
)
có dạng: F(x, y) = (x- x
a
) * (y
b
- y
a
) - (y - y
a
) * (x
b
- x
a
) = 0.
Vậy phương trình tương quan giữa điểm M(x
m
, y
m
) với đường thẳng đi
qua hai điểm A, B phân biệt là:
F(x
m
, y
m
) = (x

m
- x
a
)*( y
b
- y
a
) - (y
m
- y
a
)*(x
b
- x
a
) = 0
Nếu F(x
m
, y
m
) = 0 thì M thuộc đường thẳng đi qua A, B, ngược lại M
không thuộc đường thẳng đó [4].
Chúng ta xây dựng hàm phương trình để xác định mối tương quan của
đường thẳng với một điểm như sau:
float F(xy A, xy B, xy M)
{float T;
T = (M.x - A.x)*(B.y - A.y) - (M.y - A.y)*(B.x - A.x);
return T;
}
1.2.2. Tương quan giữa điểm và đoạn thẳng

Chúng ta biết rằng, đoạn thẳng là một phần đường thẳng. Nên mối
tương quan giữa điểm M(x
m
, y
m
) với đoạn thẳng AB, (A(x
a
, y
b
), B(x
b
, y
b
)) như
sau:
Điểm M nằm trên đoạn AB khi nó thỏa mãn 2 điều kiện sau:
1. M phải thuộc đường thẳng đi qua A, B

F (A, B, M) = 0.
2. M nằm giữa Avà B

(x
m
- x
a
)*(x
m
- x
b
) < = 0 và (y

m
- y
a
)*(y
m
- y
b
)< = 0.
1.3. Giao giữa đường thẳng, đoạn thẳng
1.3.1. Đường thẳng cắt đường thẳng
Cho hai phương trình đường thẳng (1): A1*x + B1*y + C1 = 0 và (2):
A2*x + B2*y + C2 = 0. Ta gọi mối tương quan giữa (1) và (2) được biểu
diễn qua hệ phương trình sau:
A1*x + B1*y = - C1;
11
A2*x + B2*y = - C2;
Đặt D = A1*B2 - A2*B1;
Dx = B1*C2 - B2*C1;
Dy = A2*C1 - A1* C2;
• Hai đường thẳng cắt nhau khi và chỉ khi: D

0. Tọa độ điểm giao của
hai đường thẳng là: x = Dx/D, y = Dy/D.
• Hai đường thẳng song song với nhau khi và chỉ khi: D = 0 và Dx

0
hoặc Dy

0.
• Hai đường thẳng trùng nhau khi: D = Dx = Dy = 0.

1.3.2. Đường thẳng cắt đoạn thẳng
Cho phương trình đường thẳng A1*x + B1*y + C1 = 0 (1) và đoạn
AB, A(x1, y1), B(x2, y2).
Đặt A2 = y1- y2; B2 = x2- x1;
C2 = - (A2*x1 + B2 * y1);
D = A1*B2 - A2*B1;
Dx = B1*C2 - B2*C1;
Dy = A2*C1 - A1* C2;
Mối quan hệ giữa (1) và AB được thể hiện:
* Nếu D

0 và điểm P(Dx/D, Dy/D) nằm trên đoạn AB thì (1) cắt đoạn AB.
* Nếu D

0 và điểm P(Dx/D, Dy/D) nằm ngoài đoạn AB thì (1) cắt đường
thẳng chứa AB nhưng không cắt AB.
* Nếu D = Dx = Dy = 0 thì AB trùng với (1).
* Nếu D = 0, Dx

0 hoặc Dy

0 thì AB song song với (1).
1.3.3. Đoạn thẳng cắt đoạn thẳng
Kiến thức phổ thông như sau: “Trong mặt phẳng tọa độ cho đường
thẳng d: ax + by + c = 0. Khi ấy, một trong hai nửa mặt phẳng bờ d, ngoại trừ
d, gồm các điểm có tọa độ thoả mãn bất phương trình: ax + by + c > 0. Nửa
12
mặt phẳng kia gồm các điểm có tọa độ thoả mãn bất phương trình: ax + by + c
< 0”.
Từ đó ta dễ dàng chứng minh được kết quả sau: “Điều kiện cần và đủ

để hai đoạn thẳng AB và CD cắt nhau là: A, B nằm khác phía so với đoạn CD
và C, D nằm khác phía so với đoạn AB ”.
Vậy nếu xét hai điểm M(x
3
, y
3
), N(x
4
, y
4
) thì điều kiện cần và đủ để M,
N Nằm cùng phía (khác phía) so với đường thẳng có phương trình f (x, y) =
0 là: f (x
3
, y
3
). f (x
4
, y
4
) ≥ 0 (≤ 0).
Dựa trên cơ sở đó chương trình sau sẽ kiểm tra hai đoạn thẳng có cắt nhau
không:
#include “stdio.h”
#include “conio.h”
#include “math.h”
Typedef
{
float x,y ;
}xy;

xy A, B, C, D ;
// Tinh gia tri ham F tai diem M voi F la ham ung voi phuong trinh duong
thang đi qua AB
float F(xy A, xy B, xy M)
{ float T;
T = (M.x - A.x) * (B.y - A.y) - (M.y - A.y)* (B.x - A.x);
return T;
}
int KhacPhia (xy A, xy B, xy C, xy D)
// Kiem tra xem C, D co nam khac phia so voi đuong thang đi qua A, B hay khong?
13
{ int t;
If ( F ( A,B,C) * F( A, B, D) ) < = 0 ) t = 1;
Else t = 0;
}
void main()
{printf( “ nhap toa do A, B”);
scanf(“%f%f%f%f” , &A.x, &A.y, &B.x, &B.y);
printf( “ nhap toa do C, D”);
scanf(“%f%f%f%f”,&C.x, &C.y, &D.x, &D.y);
If (khacphia(A,B,C,D) = = 1 && Khacphia (C,D,A,B) = = 1)
Printf (“Hai đoan thang cat nhau”);
Else printf (“

Khong cat nhau”);
Getch();}
1.4. Một số bài tập áp dụng
1.4.1. Kiểm tra một điểm thuộc một tia
Bài toán: kiểm tra điểm M có thuộc tia AB hay không?
Điểm M thuộc tia AB ⇔ 2 điều kiện

sau đều thoả mãn:
1. M, A, B thẳng hàng ⇔ F(A, B, M) = 0.
2. A không nằm giữa B, M ⇔ (x
m
- x
a
)*(x
b
- x
a
)

0 và (y
m
- y
a
)*(y
b
-y
a
)

0.
1.4.2. Kiểm tra một tia có cắt một đoạn không
Bài toán: kiểm tra tia AB có cắt đoạn CD không?
Có thể đưa ra 2 cách giải như sau:
Cách 1: Trước tiên tìm giao điểm M (nếu có) của 2 đường thẳng AB và CD,
khi đó tia AB cắt đoạn CD ⇔ 2 điều kiện sau đều thoả mãn:
1. M nằm giữa C, D ⇔ (x
m

- x
c
)*(x
m
- x
d
) ≤ 0 và (y
m -
y
c
)*(y
m
- y
d
) ≤ 0.
2. A không nằm giữa B, M ⇔ (x
m
- x
a
)*(x
b
- x
a
) ≥ 0 và (y
m
- y
a
)*(y
b
- y

a
) ≥ 0.
A B
. M
14
Cách 2: Tia AB cắt đoạn CD ⇔ 2 điều kiện sau thoả mãn:
1. C, D nằm khác phía so với AB ⇔ Khacphia (A, B, C, D) = True.
2. A, M nằm khác phía so với CD ⇔ Khacphia (C, D, A, M) = True.
Trong đó M(x, y) là điểm thuộc tia AB và thoả mãn điều kiện:
x
m
 > Max (x
c
,x
d
) hoặc y
m
 > Max (y
c
,y
d
);
Giả sử hàm Max đã xây dựng, có thể mô tả việc cài đặt như sau:
Void Tim _ M()
{int i;
float Xmax,Ymax;
{i = 1;
Xmax = Max(abs(C. x), abs(D. x));
Ymax = Max(abs(C. y), abs(D. y));
While (abs(A. x + i * (B.x - A.x)) < = Xmax) &&

(abs(A. y + i * (B. y - A. y)) < = Ymax) i++;
M.x = A. x

+ i * (B.x - A.x);
M.y = A. y

+ i * (B.y - A.y);
}
void Ket_qua()
{If ( Khacphia (A,B,C,D) = = 1)
{
Tim_M;
If(Khacphia(C,D,A,M) = = 1) printf (“Tia AB cat đoan CD”);
Else printf (“

Tia AB khong cat doan CD” );
}
Else printf (

“ Tia AB khong cat doan CD ”

);
}
15
void main()
{<Nhập A,B,C,D>;
Ket_ qua();
Getch();}
1.4.3. Kiểm tra tính lồi, lõm của đa giác
Bài toán: Cho file DAGIAC.INP có cấu trúc như sau: dòng đầu ghi số

nguyên n>2 là số đỉnh của đa giác, n dòng tiếp theo mỗi dòng ghi hai số thực
là tọa độ của các đỉnh tương ứng của đa giác (biết rằng n đỉnh đó theo thứ tự
tạo thành một đa giác không tự cắt). Yêu cầu kiểm tra đa giác đó là lồi hay
lõm?
Thuật toán: Gọi diện tích của đa giác là S
0.
ta sẽ so sánh S
0
với các S
i
,
trong đó S
i
là diện tích của đa giác thu được từ đa giác ban đầu sau khi bỏ đi
đỉnh thứ i (i = 1, 2, , n). Nếu S
i


> S
0
thì đa giác đã cho lõm (tại đỉnh thứ i),
ngược lại thì đa giác đã cho lồi (tính diện tích áp dụng theo công thức trong
mục 1.1.4.3.).
Chương trình như sau:
//Kiem tra mot da giac loi hay lom
#include"stdio.h"
#include"conio.h"
#include "math.h"
#include"process.h"
#include"ctype.h"

typedef struct
{
float x,y;
}xy;
xy a[200];
16
int n;
float s0,s1;
void docfile()
{ FILE *f;int i;
f = fopen("dagiac.inp","r+");
fscanf(f,"%d",&n);
for (i = 1;i< = n;i++)
fscanf(f,"%f%f",&a[i].x,&a[i].y);
a[n+1] = a[1];
s0 = 0;
for( i = 1;i< = n;i++)
s0 = s0+(a[i+1].x-a[i].x)*(a[i+1].y+a[i].y);
s0 = abs(s0)/2;
}
void inkq(int i)
{FILE *f1;
f1 = fopen("dagiac.out","w+");
fprintf(f1,"%d",i);
fclose(f1);
}
void xuly()
{xy b[100];
int t = 0;
int i,j,k;

for (i = 1;i< = n;i++)
{for (j = 1;j< = i-1;j++) b[j] = a[j];
for(j = i+1;j< = n+1;j++) b[j-1] = a[j];
17
s1 = 0;
b[n] = b[1];
for (j = 1;j< = n-1;j++)
s1 = s1+(b[j+1].x-b[j].x)*(b[j+1].y+b[j].y);
s1 = abs(s1)/2;
if( s1>s0)
{t++;
exit;
}
}
if (t = = 0) printf("dagiac loi");
else printf("da giac lom");
}
void main()
{docfile();
xuly();
getch();
}
1.4.4. Kiểm tra vị trí của một điểm với một đa giác
Bài toán: kiểm tra vị trí của một điểm M so với một đa giác
Xét đa giác có thể lồi hay lõm như hình vẽ.
A
2
M’
A
1

A
3
A
5
A
6
A
7
A
4
M
18
Để giải bài toán này ta có hai cách sau:
C1: Trước hết ta tìm M’(x, y) sao cho x > max {x
i
|x
i
là hoành độ một
đỉnh i của đa giác; i = 1, 2 }. Còn y có giá trị sao cho đoạn MM’ không
chứa đỉnh nào của đa giác, y có thể tìm bằng phương pháp duyệt trung điểm
của các cạnh hay lựa chọn ngẫu nhiên. Tiếp đó ta đếm số giao điểm của đoạn
MM’ so với tất cả các cạnh của đa giác:
+ Nếu số giao điểm là lẻ thì M nằm trong đa giác.
+ Nếu số giao điểm là chẵn thì M nằm ngoài đa giác.
Chú ý: Nếu M thuộc một cạnh hay một đỉnh nào đó của đa giác thì ta
cần xử lý riêng.
C2: Cho trước điểm M(x, y) và một đa giác P, M không nằm trên cạnh
của P. Xét nửa đường thẳng có gốc tại M, song song với trục tung Oy và
hướng theo chiều dương. Tính tổng số giao điểm của nửa đường thẳng này
với tất cả các cạnh của đa giác. Nếu số giao điểm này là lẻ thì M nằm trong đa

giác, ngược lại thì kết luận M nằm ngoài đa giác.
Thủ tục kiểm tra như sau:
//kiem tra diem x co nam trong da giac c có l canh khong
int inside(xy x, xy c[200], int l)
{int t;
int j,s,l,i,k;
s = 0;
for( i = 1;i< = l;i++)
{
j = i-1;k = i+1;
if(x.x<max(c[i].x,c[k].x) && x.x>min(c[i].x,c[k].x))
if (x.y< = min(c[i].y,c[k].y)) s = s+1;
19
else //kiem tra gia tri cua ham tai x.x co nam duoi doan thang
if( x.y< = (x.x-c[i].x)*(c[k].y-c[i].y)/(c[k].x-c[i].x)+c[i].y) s = s+1;
//truong hop di qua dinh
if (x.x = = c[i].x)
{
if (x.x! = c[j].x && x.x! = c[k].x)
if (x.x<min(c[j].x,c[k].x) ||x.x>max(c[j].x,c[k].x))
s = s+2; // x nam ve cung mot phia
else
if( x.y< = (x.x-c[i].x)*(c[k].y-c[i].y)/(c[k].x-c[i].x)+c[i].y )
s = s+1;
// trung canh c[i]c[i+2]
if (x.x = = c[k].x)
if (x.x<min(c[j].x,c[k+1].x) ||x.x>max(c[j].x,c[k+1].x))
s = s+2; //x nam ve cung mot phia
else
if (x.y * (c[k+1].x-c[i+1].x)< = (x.x-c[i+1].x)*(c[k+1].y-c[i+1].y)

+c[i+1].y * (c[k+1].x-c[i+1].x) s = s+1;
}
t = (s%2);
}
return t;
}
1.4.5. Bài toán tìm phần giao, phần hợp của hai đường tròn
Bài toán: Trong mặt phẳng tọa độ cho trước tọa độ tâm và độ dài bán
kính của hai đường tròn. Hãy tìm diện tích phần giao và diện tích phần hợp
của chúng.
20
Dữ liệu: Vào cho trong file CIRCLE.INP gồm hai dòng, mỗi dòng chứa 3 số
theo thứ tự là hoành độ, tung độ và bán kính của đường tròn.
Dữ liệu: Ra ghi vào file CIRCLE.OUT gồm hai dòng, dòng đầu tiên ghi một
số thực là diện tích phần giao, dòng tiếp theo ghi diện tích phần hợp (các số
thực được lấy chính xác đến hai chữ số thập phân).
Ví dụ:
CIRCLE.INP
0 0 1
0 0 3
CIRCLE.OUT
3.12
28.30
Thuật toán tìm diện tích phần giao (hay còn gọi là phương pháp chia
lưới): Chia mặt phẳng ra thành một lưới các ô vuông bởi các đường thẳng
song song với các trục tọa độ. Kiểm tra từng ô vuông, nếu nó nằm trong cả
hai đường tròn (thuộc phần giao của hai đường tròn) thì ta cộng thêm vào giá
trị diện tích cần tìm một lượng bằng diện tích của ô vuông đó.
Một số lưu ý với phương pháp này:
- Phương pháp này có thể mở rộng để tìm diện tích phần giao và phần

hợp của tập gồm nhiều đối tượng bất kỳ như đa giác, đường tròn, elíp Điểm
quan trọng là ta phải xác định được điều kiện để kiểm tra một ô vuông có
thuộc miền trong của đối tượng không.
- Phương pháp này chỉ cho kết quả gần đúng. Kích thước các ô vuông
của lưới càng nhỏ thì độ chính xác càng cao nhưng tốc độ thực hiện lại chậm
đi. Phương pháp lưới làm việc tốt nếu các điểm rải đều trong vùng tìm, nhưng
sẽ tồi tệ nếu các điểm cụm lại thành đám (ví dụ, tất cả các điểm có thể rơi
trong một ô, nghĩa là các ô khác sẽ không chứa gì cả).
21
- Cần có kỹ thuật duyệt để hạn chế số lượng hay khoanh vùng các ô
vuông nhỏ cần kiểm tra. Chẳng hạn, với bài toán trên thì rõ ràng ta chỉ cần
duyệt các ô vuông nằm trong một hình vuông ngoại tiếp một trong hai đường
tròn.
Thuật toán tìm phần hợp của hai đường tròn rất đơn giản là ta chỉ việc
lấy diện tích của tổng hai đường tròn rồi trừ đi phần diện tích giao nhau (hoặc
cũng có thể thay vì kiểm tra điều kiện ô vuông phải nằm trong cả hai đường
tròn thì ta chỉ cần kiểm tra nó có thuộc một đường tròn nào đó không).Để
thực hiện thao tác chia mặt phẳng thành lưới ô vuông, ta dùng hai vòng lặp
lồng nhau với hai biến điều khiển để duyệt trên các cạnh của lưới. Dưới đây là
chương trình minh hoạ cho thuật toán tìm diện tích phần giao của hai dường
tròn (để đơn giản, ta coi một ô vuông là thuộc đường tròn nếu đỉnh trên bên
trái của nó thuộc đường tròn đó).#include"stdio.h"
#include"conio.h"
#include"math.h"
#include"dos.h"
float x1,y1,x2,y2,i,j,r1,r2,k,h,s;
FILE *f;
float khoangcach(float x,float y,float u,float v)
{float t;
t = sqrt((x-u)*(x-u)+ (y-v)*(y-v));

return t;
}
void docfile()
{FILE *f;
f = fopen("circle.inp","r+");
fscanf(f,"%f%f%f",&x1,&y1,&r1);
22
fscanf(f,"%f%f%f",&x2,&y2,&r2);
fclose(f);
s = 0;
}
void ketqua()
{
f = fopen("circle.out","w+");
fprintf(f,"%4.2f\n",s);
fprintf(f,"%4.2f",M_PI*(r1*r1+r2*r2)-s);
fclose(f);
}
void xuly()
{
docfile();
if(khoangcach(x1,y1,x2,y2)<r1+r2) i = x1-r1;
k = x1+r1;
h = y1+r1;
while (i<k)
{
j = y1-r1;
while(j<h)
{
if(khoangcach(i,j,x1,y1)<r1)

if(khoangcach(i,j,x2,y2)<r2) s = s+0.0025;
j = j+0.05;
}
i = i+0.05;
23
}
ketqua();
}
void main()
{
xuly();
getch();
}
Chương trình trên có thể cải thiện tốc độ bằng cách kiểm tra các ô
vuông nằm trong hình vuông ngoại tiếp đường tròn có bán kính bé hơn.
Đến đây chúng ta có thể giải được nhiều bài toán tương tự với các đối
tượng hình học khác như giữa hai (hay nhiều) tam giác, giữa hai (hay nhiều)
hình chữ nhật hay hỗn hợp nhiều loại đối tượng với nhau, chẳng hạn tìm
giao của đường tròn và tam giác.
1.4.6. Bài toán áp dụng phương pháp lưới
Ta nói rằng đoạn AB nhìn thấy được từ điểm C dưới một góc vuông,
nếu hoặc là C nằm trên AB hoặc là trên đoạn AB có thể tìm được một điểm D
sao cho AB

CD.
Bài toán như sau: Cho một đa giác N đỉnh, hãy xác định một điểm nằm
trên đa giác (hoặc ở phần trong, hoặc ở trên cạnh) sao cho từ đó có thể nhìn
được dưới một góc vuông nhiều cạnh của đa giác nhất.
Dữ liệu: Vào từ file văn bản DAGIAC.INP:
 Dòng đầu tiên chứa số đỉnh của đa giác N (3


N

100).
 N dòng tiếp theo mỗi dòng chứa tọa độ của một đỉnh của đa giác được
liệt kê theo thứ tự đi vòng quanh đa giác theo chiều ngược kim đồng hồ.
24
Các tọa độ là các số thực có trị tuyệt đối không quá 10000. Độ dài mỗi
cạnh của đa giác đều không bé hơn 1.
Kết quả: Ghi ra file DAGIAC.OUT:
 Dòng đầu tiên ghi P là số cạnh của đa giác có thể nhìn thấy được dưới
một góc vuông từ một điểm nào đó trên đa giác.
 Dòng thứ hai ghi tọa độ của điểm nằm trên đa giác mà từ đó có thể nhìn
thấy dưới một góc vuông P cạnh của đa giác. (sai số không quá 10
-3
sẽ
được bỏ qua).
Ví dụ:
DAGIAC.INP DAGIAC.OUT
4
0 0
1 0
1 1
0 1
4
0.0000 0.0000
Để giải quyết bài toán này thì kiến thức phổ thông không thể không
biết đó là:
AB


CD

(x
b
-x
a
)*(x
d
-x
c
) + (y
b
-y
a
)*(y
d
-y
c
) = 0. Trong đó A(x
a
, y
a
),
B(x
b
, y
b
), C(x
c
, y

c
), D(x
d
, y
d
).
Phân tích bài toán: điều kiện của bài toán đặt ra không có gì phức tạp,
nhưng cái khó của bài toán này là ổ chỗ: làm thế nào để lấy được từng điểm
nằm trên đa giác ra để kiểm tra?. Phương án tốt nhất là ta dùng phương pháp
lưới như đã trình bày ở mục 1.4.5.
Thuật toán như sau:
• Tìm một hình chữ nhật chứa đa giác đã cho, hình chữ nhật đó có điểm
dưới trái A có tọa độ là (min(X), min(Y)), điểm trên phải C có tọa độ là
(max(X), max(Y)), trong đó X = {x
i
| là hoành độ thứ i của đỉnh thứ i của
25

×