You are on page 1of 25

CHUYÊN ĐỀ DUYÊN HẢI MÔN TIN HỌC

GIẢI CÁC BÀI TOÁN HÌNH HỌC TRONG TIN HỌC


MỤC LỤC

I. Mở đầu .......................................................................................................... 3

II. Những công thức hình học cơ bản .............................................................. 3

III. Một số bài tập áp dụng .............................................................................. 5

Bài 1: Dãy hình chữ nhật lồng nhau ................................................................ 5

Bài 2: Tính giao điểm ..................................................................................... 6

Bài 3: Đổi đất .................................................................................................. 9

Bài 4: Pháo đài .............................................................................................. 13

Bài 5: Đường đi ngắn nhất ............................................................................ 18

IV. Các bài tập tham khảo thêm ................................................................... 22

Bài 1: Tổng véctơ.......................................................................................... 22

Bài 2: Đếm hình chữ nhật ............................................................................. 23

Bài 3: Điểm và đa giác .................................................................................. 23

Bài 4. Xây dựng đường ................................................................................. 23

Bài 5: Nông trại ............................................................................................ 24

V. Kết luận ...................................................................................................... 25

2
CHUYÊN ĐỀ GIẢI CÁC BÀI TOÁN HÌNH HỌC TRONG TIN HỌC

I. Mở đầu
Các bài toán hình học trong tin học rất đa dạng và phong phú. Mỗi bài có
thể kết hợp nhiều thuật toán cơ bản khác nhau ở các chuyên đề như Quy hoạch
động, Đồ thị,… kết hợp với các công thức hình học giải tích cơ bản cho ra kết
quả tối ưu của bài toán. Trong chuyên đề này tôi trình bày một số bài toán hình
học có các cách kết hợp các thuật toán khác nhau.

II. Những công thức hình học cơ bản


1. Khoảng cách giữa 2 điểm A(x1,y1), B(x2,y2) trong mặt phẳng

d  ( x1  x2)2  ( y1  y 2)2
2. Khoảng cách từ một điểm M(x0,y0) đến đường thẳng d có phương trình
tổng quát ax + by + c = 0:
ax0  by0  c
h
a 2  b2
3. Cho 3 điểm phân biệt A(x1,y1), B(x2,y2), M(x3,y3) cùng nằm trên một
đường thẳng, xét M có nằm giữa A, B hay không?
Điều kiện là: (x1 - x3)(x3 - x2) >0 hoặc (y1 - y3)(y3 - y2) >0
4. Phương trình đường thẳng đi qua hai điểm A(x1,y1), B(x2,y2):
x  x1 y  y1

x 2  x1 y 2  y1
đưa về dạng tổng quát: (y1 - y2)x + (x2 - x1)y + (x1y2 - x2y1) = 0
5. Vị trí tương đối giữa hai đường thẳng d1: a1x + b1y = c1,
d2: a2x + b2y = c2
Ta tính các định thức:
D = a1b2 - a2b1; Dx = c1b2 - c2b1; Dy = a1c2 - a2c1.
Nếu (D = 0) và (Dx = 0) và (Dy = 0) thì d1 và d2 trùng nhau;
Nếu (D =0) và ((Dx <> 0) hoặc (Dy <> 0)) thì d1 song song với d2;
Nếu D <> 0 thì hai đường thẳng cắt nhau tại x=Dx/D; y=Dy/D.
6. Hai đoạn thẳng giao nhau
Cho hai đoạn thẳng được xác định bởi d1: A(x1,y1), B(x2,y2),
d2: (x3,y3), D(x4,y4).
a, Cách 1
3
Tìm giao điểm của 2 đường thẳng AB và CD, sau đó kiểm tra xem giao
điểm có thuộc đồng thời cả hai đoạn thẳng AB và CD hay không.
b, Cách 2
Xây dựng hai phương trình đường thẳng đi qua AB (F) và CD (G). Nếu
AB cắt CD thì C, D nằm về hai phía của đường thẳng AB tức là
F(x3,y3)*F(x4,y4)<0. Còn nếu CD cắt AB thì A, B nằm về hai phía của đường
thẳng CD tức là G(x1,y1)*G(x2,y2)<0.
Vậy để đoạn thẳng AB giao với đoạn thẳng CD ta cần có:
F(x3,y3)*F(x4,y4)<0 và G(x1,y1)*G(x2,y2)<0
7. Công thức tính diện tích đa giác không tự cắt
Cho đa giác không tự cắt A có n đỉnh: A1, A2, ..., An, công thức tính diện
tích đa giác là:
n n
s (x  x
i 1
i i 1 )(yi  yi 1 ) / 2 hoặc s  ( y
i 1
i  yi 1 )(xi  xi 1 ) / 2

trong đó tọa độ đỉnh 1 và đỉnh n+1 trùng nhau.

4
III. Một số bài tập áp dụng
Bài 1: Dãy hình chữ nhật lồng nhau
Trên mặt phẳng tọa độ cho N hình chữ nhật với các cạnh song song với hệ
trục tọa độ, các hình chữ nhật được đánh số từ 1 tới N. Hình chữ nhật thứ i được
cho bởi 4 số nguyên dương xi1, yi1, xi2, yi2, trong đó (xi1, yi1) là tọa độ đỉnh trái
dưới, còn (xi2, yi2) là tọa độ đỉnh phải trên. Ta nói rằng hình chữ nhật thứ i nằm
trong hình chữ nhật thứ j nếu trên mặt phẳng tọa độ, mọi điểm của hình chữ nhật
i đều thuộc hình chữ nhật j.
Yêu cầu: Với N hình chữ nhật cho trước, hãy tìm K hình chữ nhật với chỉ số i1,
i2, …, iK sao cho hình i1 nằm trong hình i2, hình i2 nằm trong hình i3, …, hình iK-1
nằm trong hình iK và K là lớn nhất.
Dữ liệu: Vào từ file văn bản HCN.INP:
 Dòng đầu tiên chứa số nguyên dương N (1  N  100).
 N dòng tiếp theo, dòng thứ i chứa 4 số nguyên dương xi1, yi1, xi2, yi2 có giá
trị không vượt quá 30000.
Kết quả: Ghi ra file văn bản HCN.OUT số K tìm được.
Ví dụ:
HCN.INP HCN.OUT
3 2
1174
3166
2254

Thuật toán:
- Sắp xếp các hình chữ nhật giảm dần theo diện tích.
- Áp dụng thuật toán QHĐ - dãy con giảm dài nhất để tìm dãy các hình
chữ nhật lồng nhau.
Chương trình:
uses math;
const
finp='hcn.inp';
fout='hcn.out';
MAXN=100;

type hcn=record x1,y1,x2,y2: longint; end;

var
h: array[1..MAXN] of hcn;
f: array[1..MAXN] of longint;
i,j,n,kq: longint;
5
tam:hcn;

function dientich(a:hcn):longint;
begin
with a do dientich:=(x2-x1)*(y2-y1);
end;

function bao(a,b:hcn):boolean;
begin
bao:=(a.x1<=b.x1) and (b.x2<=a.x2) and
(a.y1<=b.y1) and (b.y2<=a.y2);
end;

begin
assign(input,finp);
reset(input);
assign(output,fout);
rewrite(output);

readln(n);
for i:=1 to n do
with h[i] do
readln(x1,y1,x2,y2);
for i:=1 to n-1 do
for j:=i+1 to n do
if (dientich(h[i])<dientich(h[j])) then
begin
tam:=h[i];h[i]:=h[j];h[j]:=tam;
end;
kq:=0;
for i:=1 to n do
begin
f[i]:=1;
for j:=i-1 downto 1 do
if bao(h[j],h[i]) then
f[i]:=max(f[i],f[j]+1);

if (f[i]>kq) then kq:=f[i];


end;
writeln(kq);

close(input);
close(output);
end.
Test:
https://www.mediafire.com/file/jwypuuv1uji53n3/CD+HH2.rar

Bài 2: Tính giao điểm


Cho một hình chữ nhật có kích thước MxN (M, N<=1000). Hình chữ nhật
đó được chia thành MxN ô vuông bởi các đường thẳng song song với các cạnh
của hình chữ nhật (hình chữ nhật khi đó có M dòng và N cột). Thứ tự các dòng

6
được đánh số từ 1 đến M từ trên xuống dưới, thứ tự các cột được đánh số thừ 1
đến N từ trái sang phải. Khi đó ta nói ô có toạ độ (i,j) là ô nằm ở trên dòng i và
cột j. Hình chữ nhật này có 2 đường chéo, đường chéo 1 nối ô (1,1) với ô (M,N),
đường chéo 2 nối ô (M,1) với ô (1,N). Một ô của hình chữ nhật gọi là bị cắt bởi
một đường chéo nếu nó có từ hai điểm chung với đường chéo đó trở lên.
Với mỗi bộ ba số M, N, K cho trước trong đó M, N là kích thước hình chữ
nhật, còn k=1, 2 là tên đường chéo của hình chữ nhật. Hãy thông báo số ô của
hình chữ nhật và toạ độ của các ô đó mà bị cắt bởi đường chéo.
Dữ liệu vào file CAT.INP gồm một bộ 3 số M, N, K mỗi số cách nhau ít
nhất một dấu cách.
Kết quả ra file CAT.OUT ghi thông tin ra được ghi trên 2 dòng, dòng thứ
nhất ghi số ô bị cắt, dòng thứ 2 ghi toạ độ các ô bị cắt theo thứ tự tăng dần của
chỉ số dòng, chỉ số cột của các ô.
Ví dụ:

CAT.INP CAT.OUT
341 6
(1,1) (1,2) (2,2) (2,3) (3,3) (3,4)
Thuật toán:
- Thành lập phương trình đường thẳng là đường chéo của hình chữ nhật
loại k.
- Xét đường thẳng này có cắt ô (i,j) của hình chữ nhật hay không?
Chương trình:
const fi='cat.inp';
fo='cat.out';
nm=10000;
type hcn=record
x,y:integer;
end;
var a:array[1..nm]of hcn;
i,j,n,m,k,a1,b1,c1,dem:longint;
f:text;
procedure nhap;
begin
assign(f,fi);reset(f);
readln(f,m,n,k);
close(F);
if k=1 then
begin
a1:=-m;

7
b1:=-n;
c1:=0;
end
else
begin
a1:=m;
b1:=-n;
c1:=-m*n;
end;
end;
function xetcat(i,j:longint):boolean;
var a2,b2,c2,d,dx,dy,dem1,dem2:longint;x,y:real;
begin
dem1:=0;
dem2:=0;
a2:=1;
b2:=0;
c2:=j-1;
d:=a1*b2-a2*b1;
dy:=a1*c2-a2*c1;
y:=dy/d;
if (y>-i)and(y<-i+1) then inc(dem1);
if (y=-i)or(y=-i+1) then inc(dem2);
a2:=1;
b2:=0;
c2:=j;
d:=a1*b2-a2*b1;
dy:=a1*c2-a2*c1;
y:=dy/d;
if (y>-i)and(y<-i+1) then inc(dem1);
if (y=-i)or(y=-i+1) then inc(dem2);
a2:=0;
b2:=1;
c2:=i-1;
d:=a1*b2-a2*b1;
dx:=c1*b2-c2*b1;
x:=dx/d;
if (x>-j)and(x<-j+1) then inc(dem1);
if (x=-j)or(x=-j+1) then inc(dem2);
a2:=0;
b2:=1;
c2:=i;
d:=a1*b2-a2*b1;
dx:=c1*b2-c2*b1;
x:=dx/d;
if (x>-j)and(x<-j+1) then inc(dem1);
if (x=-j)or(x=-j+1) then inc(dem2);
if (dem1>=1)or(dem2>=3) then xetcat:=true
else xetcat:=false;
end;
procedure print;
begin
writeln(f,dem);

8
for i:=1 to dem do
with a[i] do
write(f,'(',x,',',y,') ');
end;
procedure xuly;
begin
assign(f,fo);rewrite(f);
dem:=0;
for i:=1 to m do
for j:=1 to n do
if xetcat(i,j) then
begin
inc(dem);
a[dem].x:=i;
a[dem].y:=j;
end;
print;
close(F);
end;

begin
nhap;
xuly;
end.
Test:
https://www.mediafire.com/file/jwypuuv1uji53n3/CD+HH2.rar
Bài 3: Đổi đất
Hai bộ lạc Anpha và Bêta sống rất hoà thuận với nhau. Một phần ranh
giới của hai bộ lạc là một đường gấp khúc không tự cắt. Đường gấp khúc nhận
được bằng cách lần lượt nối N điểm đôi một khác nhau A1, A2,......,AN. Điểm Ai
được xác định bởi hoành độ xi và tung độ yi (xi là các số nguyên thoả mãn điều
kiện xi <= xi+1). Phần đất của bộ lạc Anpha nằm ở phía trên đường gấp
khúc.Nhân dịp năm mới, tù trưởng hai bộ lạc quyết định thay đổi đường ranh
giới cũ bằng cách xây dựng một đường cao tốc là đường nối thẳng từ A1 tới AN
và lấy đường cao tốc này làm ranh giới mới. Dĩ nhiên, sự thay đổi này sẽ chuyển
một số phần đất của bộ lạc Anpha cho bộ lạc Bêta và ngược lại. Hai tù trưởng
thoả thuận phần diện tích chênh lệch do việc thay đổi đường ranh giới sẽ được
điều chỉnh trong tương lai bằng một cách khác.
Yêu cầu : Hãy tính diện tích phần đất SA của bộ lạc Anpha trở thành đất của bộ
lạc Bêta và diện tích phần đất SB của bộ lạc Bêta trở thành đất của bộ lạc Anpha
sau khi thay đổi đường ranh giới giữa hai bộ lạc.
Dữ liệu : Vào từ file văn bản DOIDAT.INP trong đó :
- Dòng đầu chứa số N (N<=10000).

9
- Dòng thứ i trong N dòng tiếp theo chứa hai số nguyên xi và yi đặt cách
nhau ít nhất một dấu cách. (-32000 <=xi, yi <= 32000).
Kết quả : Đưa ra file văn bản DOIDAT.OUT trong đó dòng thứ nhất chứa SA,
dòng thứ hai chứa SB. Kết quả được lấy chính xác với 4 chữ số sau chấm thập
phân.

Ví dụ: DOIDAT .INP DOIDAT .OUT


6 8.0000
0 0 9.0000
2 4
5 1
7 11
10 8
11 11

Thuật toán:
- Thành lập phương trình đường thẳng theo cách chia đất.
- Tính diện tích các đa giác con theo tọa độ mà đường thẳng đã phân chia
thành phía trên và phía dưới của đường thẳng phân đất.
Chương trình:
Const fi = 'Doidat.inp';
fo = 'Doidat.out';
maxn = 10010;

Type Point = record


x,y:real;
end;

M1 = array [1..maxn] of real;


M2 = array [1..maxn] of Integer;

Var Tx,Ty:M1;
Ox,Oy:M2;
a,b,c,Sa,Sb,S:Real;
N,Sd:Integer;
f,g:Text;

Procedure DocDl;
Var i:Integer;
Begin
Assign(f,fi);
Reset(f);
Readln(f,N);
For i:=1 to n do
Readln(f,Ox[i],Oy[i]);
Close(f);

10
End;

Procedure LapPTDT(D1,D2:Point;Var a,b,c:Real);


Begin
a:=D2.y-D1.y;
b:=D1.x-D2.x;
c:=D1.y*D2.x-D1.x*D2.y;
End;

FuncTion GiaTri(D:Point):Real;
Begin
GiaTri:=A*D.x + B*D.y + C;
End;

FuncTion Cut(D1,D2:Point):Boolean;
Var F1,F2:Real;
Begin
F1:=GiaTri(D1);
F2:=GiaTri(D2);
If (F1=0) And (F2=0) Then
Begin
Cut:=False;
Exit;
End;
Cut:= F1*F2<=0;
End;

Procedure TimGiao(D1,D2:Point;var Gd:Point);


Var D,Dx,Dy:Real;
a1,b1,c1:Real;
Begin
LapPTDT(D1,D2,a1,b1,c1);
D:=a*b1-a1*b;
Dx:=b*c1-b1*c;
Dy:=c*a1-c1*a;
If D<>0 Then
Begin
Gd.x:=Dx/D;
Gd.y:=Dy/D;
End;
End;

Procedure ChuanBi;
Var D1,Dn:Point;
Begin
SA:=0;
SB:=0;
D1.x:=Ox[1];
D1.y:=Oy[1];
Dn.x:=Ox[N];
Dn.y:=Oy[N];
LapPTDT(D1,Dn,a,b,c);
End;

11
Procedure TinhDienTich;
Var i:Integer;
Begin
S:=0;
Tx[Sd+1]:=Tx[1];
Ty[Sd+1]:=Ty[1];
For i:=1 To Sd Do
S:=S+(Ty[i]+Ty[i+1])*(Tx[i]-Tx[i+1]);

S:=Abs(S)/2;
End;

Procedure XacDinhMien(Var TT:Integer);


Var i:Integer;
Begin
For i:=2 to Sd-1 do
If A*Tx[i] + B*Ty[i] + C <>0 Then
If A*Tx[i] + B*Ty[i] + C >0 Then TT:=2
Else TT:=1;
End;

Procedure XulyDaGiac;
Var TT,i,j:Integer;
Begin
TinhDienTich;
XacDinhMien(TT);
If TT=2 Then SA:=SA+S
Else
If TT=1 Then SB:=SB+S;
End;

Procedure Tinh;
Var i,j:Integer;
D1,D2,Gd:Point;
Begin
Tx[1]:=Ox[1];
Ty[1]:=Oy[1];
SD:=1;
For i:=2 to N-1 do
Begin
Inc(Sd);
Tx[Sd]:=Ox[i];
Ty[Sd]:=Oy[i];

D1.x:=Ox[i];
D1.y:=Oy[i];
D2.x:=Ox[i+1];
D2.y:=Oy[i+1];

If Cut(D1,D2) then
Begin

12
TimGiao(D1,D2,Gd);
Inc(Sd);
Tx[Sd]:=Gd.x;
Ty[Sd]:=Gd.y;

XulyDaGiac;

Sd:=1;
Tx[1]:=Gd.x;
Ty[1]:=Gd.y;

End;
End;

Inc(Sd);
Tx[Sd]:=Ox[n];
Ty[Sd]:=Oy[n];
XulyDaGiac;
End;

Procedure Ghikq;
Begin
Assign(g,fo);
Rewrite(g);
Writeln(G,Sa:0:4);
Writeln(G,Sb:0:4);
Close(g);
End;

Begin
Docdl;
ChuanBi;
Tinh;
Ghikq;
End.
Test:
https://www.mediafire.com/file/jwypuuv1uji53n3/CD+HH2.rar
Bài 4: Pháo đài
Một vương quốc có N pháo đài đánh số từ 1 đến N, (3<=N <= 1000. Mỗi
pháo đài được xác định bởi toạ độ trên mặt phẳng toạ độ là hai số thực, không có
3 pháo đài nào thẳng hàng. Việc bảo vệ được bố trí như sau:
Vòng 1 gồm các phá đài là đa giác có đỉnh là chúng bao mọi pháo đài
khác và có chu vi nhỏ nhất trong mọi đa giác như vậy. Với mỗi pháo đài thuộc
vòng 1 một lính được gửi đến.
Vòng 2 gồm các pháo đài chọn theo cách chọn vòng 1 đối với các pháo
đài không thuộc vòng 1, với mỗi pháo đài thuộc vòng 2 hai lính được gửi đến.

13
Dữ liệu vào file PHAODAI.INP: Dòng đầu ghi số N, dòng thứ i trong N
dòng tiếp theo ghi toạ độ pháo đài thứ i.
Kết quả ghi ra file PHAODAI.OUT: Gồm N dòng, dòng thứ i là số lính
gửi đến pháo đài

Ví dụ:
PHAODAI.INP PHAODAI.OUT

16 1
82 1
24 1
17 4 2
65 3
87 3
11 9 2
15 10 2
12 18 2
5.1 16 1
4 20 1
15 18 3
7 15 3
6 14 4
11 13 43
12 13
13 14

Thuật toán:
- Áp dụng thuật toán bao lồi nhiều lần, cho đến khi tất cả các điểm đã
được vào một bao lồi nào đó.
Chương trình:
const fi='phaodai.inp';
fo='phaodai.out';
nm=10001;
type toado=record
x,y:longint;
end;

14
mtoado=array[1..nm]of toado;
var a,b:mtoado;
c,e:array[1..nm]of boolean;
l,d:array[1..nm]of integer;
dem,vt,i,j,n,vti,vtj,x0,y0,lp:integer;
f:text;
function kttg(i,j,k:integer):boolean;
begin
kttg:=(a[j].y-a[i].y)*a[k].x+(a[i].x-
a[j].x)*a[k].y+a[j].x*a[i].y-a[i].x*a[j].y=0;
end;

function kt(n,x,y:longint):boolean;
var i,j,k:integer;
begin
for j:=1 to n-1 do
if (x=a[j].x)and(y=a[j].y) then
begin
kt:=false;
exit;
end;
for i:=1 to n-2 do
for j:=i+1 to n-1 do
for k:=j+1 to n do
if kttg(i,j,k) then
begin
kt:=false;
exit;
end;
kt:=true;
end;
procedure nhap;
var i,j,k:integer;s1,s2:string;
begin

assign(f,fi);reset(f);
readln(f,n);
for i:=1 to n do
begin
readln(f,a[i].x,a[i].y);
d[i]:=i;
end;
a[n+1]:=a[1];
close(F);
end;
function cungphia(i1,j:integer):boolean;
var i,d:integer;gt,a1,b1,c1:real;
begin
cungphia:=true;
a1:=b[j].y-a[i1].y;
b1:=a[i1].x-b[j].x;
c1:=b[j].x*a[i1].y-a[i1].x*b[j].y;
gt:=0;

15
d:=0;
for i:=1 to n do
if e[i] then inc(d);
if d=2 then exit;
i:=1;
repeat
if e[i] then gt:=a1*a[i].x+b1*a[i].y+c1;
inc(i);
until (gt<>0)or(i>=n);
if gt=0 then begin cungphia:=false;exit;end;
if gt>0 then
begin
for i:=1 to n do
if e[i] then
if a1*a[i].x+b1*a[i].y+c1<0 then begin
cungphia:=false;exit;end
end
else
begin
for i:=1 to n do
if e[i] then
if a1*a[i].x+b1*a[i].y+c1>0 then begin
cungphia:=false;exit;end
end;
end;
procedure sapxep;
var i,j,t:integer;tg:toado;
begin
for i:=1 to n-1 do
for j:=i+1 to n do
if (a[i].y>a[j].y)or((a[i].y=a[j].y)and(a[i].x>a[j].x))
then
begin
tg:=a[i];
a[i]:=a[j];
a[j]:=tg;
t:=d[i];
d[i]:=d[j];
d[j]:=t;
end;
end;
procedure xuly;
var kt:boolean;
begin
fillchar(c,sizeof(c),true);
fillchar(e,sizeof(e),true);
sapxep;
vt:=1;
lp:=0;
repeat
while not(c[vt])and(vt<=n) do inc(vt);
if vt=n+1 then break;
inc(lp);

16
dem:=1;
b[1]:=a[vt];
l[d[vt]]:=lp;
c[vt]:=false;
repeat
kt:=true;
for i:=vt+1 to n do
if c[i] then
if cungphia(i,dem) then
begin

dt(trunc(b[dem].x),trunc(b[dem].y),trunc(a[i].x),trunc(a[i].y)
);
inc(dem);
b[dem]:=a[i];
c[i]:=false;
l[d[i]]:=lp;
kt:=false;
end;
until kt;
b[dem+1]:=b[1];
e:=c;
until false;
end;
procedure print;
var i,j,tg:integer;
begin
assign(f,fo);rewrite(f);
writeln(f,lp);
for i:=1 to n-1 do
for j:=i+1 to n do
if d[i]>d[j] then
begin
tg:=d[i];
d[i]:=d[j];
d[j]:=tg;
end;
for i:=1 to n do
writeln(f,l[i]);
close(f);
end;
begin
nhap;
xuly;
print;
end.
Test:
https://www.mediafire.com/file/jwypuuv1uji53n3/CD+HH2.rar

17
Bài 5: Đường đi ngắn nhất
Một kho hàng chứa n công-tơ-nơ, có bản đồ được mô tả trong góc phần tư
thứ I của mặt phẳng tọa độ Oxy. Trong kho các công-tơ-nơ được xếp sao cho
các hình chữ nhật đáy có các cạnh song song với các trục toạ độ và giữa hai
công-tơ-nơ bất kỳ luôn tồn tại lối đi. Các hình chữ nhật đáy của các công-tơ-nơ
được mô tả bởi tọa độ đỉnh trái trên và đỉnh phải dưới. Bác bảo vệ muốn di
chuyển từ điểm có tọa độ (x1; y1) đến điểm có tọa độ (x2; y2), để đỡ mất thời
gian bác phải tìm một đường đi nào đó ngắn nhất mà không được trèo qua các
công-tơ-nơ đang đặt trong kho.
Yêu cầu: Hãy giúp bác tìm đường đi ngắn nhất từ điểm (x1; y1) đến điểm (x2; y2)
trong kho.
Dữ liệu vào từ tệp văn bản DDNN.INP:
- Dòng đầu là số n (1 ≤ n ≤ 200);
- Dòng thứ hai là x1, y1;
- Dòng thứ ba là x2, y2;
- Dòng thứ i trong n dòng tiếp theo ghi bốn số lxi, lyi, rxi, ryi (lxi< rxi, lyi>
ryi) trong đó (lxi ; lyi) là tọa độ góc trái trên, (rxi ; ryi) là tọa độ góc phải dưới
của hình chữ nhật đáy của công-tơ-nơ thứ i.
(Các tọa độ có giá trị nguyên không âm, không vượt quá 104.)
Kết quả ra ghi vào tệp văn bản DDNN.OUT: Một số duy nhất là độ dài đường
đi ngắn nhất từ điểm (x1; y1) đến điểm (x2; y2). Kết quả lấy độ chính xác 3 chữ
số sau dấu chấm thập phân.
Ví dụ:
DDNN.INP DDNN.OUT
2 14.819
00
10 10
1552
6 10 8 7
Thuật toán:
- Để có độ dài ngắn nhất thì đường đi phải đi qua các đỉnh của các hình chữ
nhật.
- Xây dựng đồ thị:
+ Coi mỗi đỉnh của một hình chữ nhật là một đỉnh của đồ thị.
+ Tìm trọng số nối 2 đỉnh thuộc 2 hình chữ nhật khác nhau nếu đường nối
này không cắt cạnh nào đó của các hình chữ nhật thì 2 đỉnh có đường nối trực
tiếp (trọng số được tính bằng khoảng cách từ đỉnh thứ nhất đến đỉnh thứ hai).
- Áp dụng thuật toán DIJKSTRA tìm đường đi ngắn nhất đi từ đỉnh có tọa độ
(x1,y1) đến đỉnh có tọa độ (x2,y2).
Chương trình:

18
const fi='DDNN.INP';
fo='DDNN.OUT';
nm=10000;
type
hcn=record
x1,y1,x2,y2,x3,y3,x4,y4:real;
end;
xy=record
x,y:real;
end;
var a:array[1..nm]of hcn;
b:array[1..nm]of xy;
c:array[1..nm,1..nm]of real;
d:array[1..nm] of real;
db:array[0..nm,0..nm]of boolean;
t:array[1..nm]of longint;
i,j,n,m,vt,h1,h2:integer;
dd,max,x1,y1,x2,y2:real;
f:text;
procedure nhap;
begin
assign(f,fi); reset(f);
readln(f,n);
readln(f,x1,y1);
readln(f,x2,y2);
for i:=1 to n do
with a[i] do
begin
readln(f,x1,y1,x3,y3);
x2:=x3; y2:=y1;
x4:=x1; y4:=y3;
end;
close(f);
end;
function kc(i,j:integer):real;
begin
kc:=sqrt(sqr(b[i].x-b[j].x)+sqr(b[i].y-b[j].y));
end;
function dtcn(x1,y1,x2,y2,x3,y3,x4,y4:real):boolean;
begin
if ((((y1-y2)*x3+(x2-x1)*y3+x1*y2-x2*y1)*((y1-y2)*x4+(x2-
x1)*y4+x1*y2-x2*y1)<0)
and(((y3-y4)*x1+(x4-x3)*y1+x3*y4-x4*y3)*((y3-y4)*x2+(x4-
x3)*y2+x3*y4-x4*y3)<0))
then dtcn:=true
else dtcn:=false;
end;
function ktcat(i,j,k:integer):boolean;
begin
with a[k] do
begin
if dtcn(b[i].x,b[i].y,b[j].x,b[j].y,x1,y1,x2,y2)
then

19
begin
ktcat:=true;
exit;
end;
if dtcn(b[i].x,b[i].y,b[j].x,b[j].y,x2,y2,x3,y3)
then
begin
ktcat:=true;
exit;
end;
if dtcn(b[i].x,b[i].y,b[j].x,b[j].y,x3,y3,x4,y4)
then
begin
ktcat:=true;
exit;
end;
if dtcn(b[i].x,b[i].y,b[j].x,b[j].y,x1,y1,x4,y4)
then
begin
ktcat:=true;
exit;
end;
if dtcn(b[i].x,b[i].y,b[j].x,b[j].y,x1,y1,x3,y3)
then
begin
ktcat:=true;
exit;
end;
if dtcn(b[i].x,b[i].y,b[j].x,b[j].y,x2,y2,x4,y4)
then
begin
ktcat:=true;
exit;
end;
end;
ktcat:=false;
end;
function kt(i,j:integer):boolean;
var k:integer;
begin
for k:=1 to n do
if ktcat(i,j,k) then
begin
kt:=false;
exit;
end;
kt:=true;
end;
procedure taodothi;
begin
dd:=100000000;
for i:=1 to m-1 do
for j:=i+1 to m do

20
begin
c[i,j]:=dd;
c[j,i]:=dd;
if kt(i,j) then
begin
c[i,j]:=kc(i,j);
c[j,i]:=c[i,j];
end;
end;
end;
procedure dijkstra;
var e:array[1..nm]of boolean;
begin
fillchar(e,sizeof(e),true);
fillchar(t,sizeof(t),0);
e[m]:=false;
for i:=1 to m-1 do
if c[m,i]<dd then
begin
d[i]:=c[m,i];
t[i]:=m;
end
else d[i]:=dd;

repeat
max:=dd;
for i:=1 to m-1 do
if (max>d[i])and(e[i]) then
begin
max:=d[i];
vt:=i;
end;
if max=dd then break;
e[vt]:=false;
for i:=1 to m-1 do
if (d[i]>d[vt]+c[vt,i])and(e[i]) then
begin
d[i]:=d[vt]+c[vt,i];
t[i]:=vt;
end;
until max=dd;

end;
procedure xuly;
begin
m:=1;
b[1].x:=x1; b[1].y:=y1;
for i:= 1 to n do
with a[i] do
begin
inc(m);b[m].x:=x1;b[m].y:=y1;
inc(m);b[m].x:=x2;b[m].y:=y2;
inc(m);b[m].x:=x3;b[m].y:=y3;

21
inc(m);b[m].x:=x4;b[m].y:=y4;
end;
inc(m);b[m].x:=x2;b[m].y:=y2;
taodothi;
dijkstra;
end;
procedure print;
begin
assign(f,fo); rewrite(f);
writeln(f,d[1]:0:3);
end;
begin
nhap;
xuly;
print;
end.
Test:
https://www.mediafire.com/file/jwypuuv1uji53n3/CD+HH2.rar

IV. Các bài tập tham khảo thêm


Bài 1: Tổng véctơ
Véctơ và các phép toán với véctơ được sử dụng để giải quyết nhiều bài
toán trong hình học, vật lý,...Ta có thể biểu diễn véctơ trong mặt phẳng bởi cặp
hai số (x,y). Các số x, y được gọi là các cặp toạ độ của véctơ.
Tổng của hai hay nhiều véctơ là véctơ mà các toạ độ của nó là tổng của
các toạ độ tương ứng của tất cả các véctơ trong tổng, nghĩa là tổng của n (n>=2)
véctơ (x1,y1), (x2,y2),....(xn,yn) là véctơ (x,y) với:
n n
x   xi , y   y i .
i 1 i 1
Ta gọi trọng lượng của véctơ (x,y) là số w(x,y)= x2 + y2.
Ví dụ cho 3 véctơ (-1,3), (2,-5) và (4,-2), khi đó ta có tổng của chúng là
véctơ:
(-1,3) + (2,-5) + (4,-2) = (-1 + 2 + 4 , 3 - 5 - 2) = (5,-4) có trọng lượng là
41.
Yêu cầu: Cho n véctơ trên mặt phẳng: (x1,y1), (x2,y2),....(xn,yn). Cần tìm
tập con (gồm ít nhất hai véctơ) của tập các véctơ đã cho với trọng lượng của
tổng tất cả các véctơ trong nó là lớn nhất.
Dữ liệu vào file VECTO.INP: dòng đầu là số nguyên dương n
(2<=n<=10.000); n dòng tiếp theo mô tả n véctơ đã cho (mỗi dòng chứa một cặp
toạ độ của một véctơ. (các toạ độ là các số nguyên có giá trị tuyệt đối không quá
30.000. Trong số các véctơ đã cho không có véctơ nào có là (0,0). Các số cùng
một dòng được ghi cách nhau bởi dấu cách.
Kết quả ra file VECTO.OUT gồm trọng lượng véctơ tổng của các véctơ
trong tập con tìm được.

22
Ví dụ:
VECTO.INP VECTO.OUT
5 202
5 -8
-4 2
4 -2
21
-6 4

Bài 2: Đếm hình chữ nhật


Trong hệ trục toạ độ ĐềCác, cho N (4<=N<=300) điểm phân biệt
A1(x1,y1), A2(x2,y2), ...,An(xn,yn).
Yêu cầu: Đếm xem có bao nhiêu hình chữ nhật khác nhau được tạo ra bởi 4
điểm bất kỳ trong N điểm trên (hai hình chữ nhật khác nhau khi chúng khác
nhau ít nhất là một đỉnh).
Dữ liệu vào file CCHUNHAT.INP: Dòng đầu là số N, N dòng tiếp theo
là toạ độ của N điểm trên.
Kết quả ra file CCHUNHAT.OUT: Ghi số lượng hình chữ nhật khác
nhau đã đếm được.
Ví dụ:
CCHUNHAT.INP CCHUNHAT.INP
7 2
00
50
05
55
10 10
10 0
0 10

Bài 3: Điểm và đa giác


Cho đa giác A gồm n (n<=1000) đỉnh, mỗi đỉnh là tọa độ nguyên có giá
trị tuyệt đối không quá 104 và điểm M(x,y). Hãy kiểm tra xem M nằm trong (1)
hay trên cạnh (2) hay trùng vào đỉnh (3) hay nằm ngoài (4) đa giác A?
Dữ liệu vào file DAGIACM.INP: Dòng đầu là số n và tọa độ điểm M; n
dòng tiếp theo là các tọa độ của đa giác.
Kết quả ra file DAGIACM.OUT: Ghi chỉ số tương ứng với các trường
hợp đã nêu.

Bài 4. Xây dựng đường


Bản đồ thành phố mới có một mạng lưới đường giao thông gồm n nút
giao thông và m đoạn đường hai chiều nối giữa các nút. Mỗi nút giao thông có
tọa độ nguyên dương trên bản đồ. Chính quyền thành phố mong muốn xây dựng
một hệ thống đường giao thông phục vụ cho khách du lịch sao cho, khách du

23
lịch xuất phát từ một nút giao thông nào đó đi thăm quan tất cả các đoạn đường,
mỗi đoạn đường duy nhất một lần và có thể quay về điểm xuất phát. Tuy nhiên,
với mạng lưới đường giao thông hiện có, khách du lịch chưa thể thực hiện được
điều này.
Yêu cầu: Từ hiện trạng hệ thống đường giao thông như trên, hãy lập trình tính
giúp chính quyền thành phố xây dựng thêm các đoạn đường nối giữa các nút
giao thông hiện có theo hai trường hợp sau:
- Trường hợp 1: Nếu hệ thống đường giao thông hiện có liên thông thì tính số
đoạn đường cần xây dựng thêm là ít nhất sao cho tổng độ dài của chúng là nhỏ
nhất đáp ứng mong muốn trên (độ dài của mỗi đoạn đường nối hai nút giao
thông là khoảng cách hình học giữa hai nút đó).
- Trường hợp 2: Nếu hệ thống đường giao thông hiện có chưa liên thông thì tính
số lượng đoạn đường cần xây dựng thêm là ít nhất đáp ứng mong muốn trên.
Chú ý: Giữa hai nút giao thông có thể có nhiều hơn một đoạn đường nối giữa
chúng.
Dữ liệu vào từ tệp XDDUONG.INP:
- Dòng đầu chứa 2 số nguyên dương n, m (1< n ≤ 500, 1< m ≤ 105);
- Trong n dòng tiếp theo chứa tọa độ (xi; yi) của các nút giao thông (1≤ i ≤ n, 0
< xi, yi ≤ 105).
- Trong m dòng tiếp theo, mỗi dòng ghi hai số nguyên dương u, v thể hiện đoạn
đường hai chiều nối hai nút giao thông u và v (1 ≤ u, v ≤ n, u ≠ v).
Kết quả ra ghi vào tệp XDDUONG.OUT:
- Dòng đầu ghi số lượng đoạn đường cần xây dựng thêm;
- Dòng thứ hai ghi tổng độ dài của các đoạn đường cần xây dựng thêm nếu hệ
thống đường giao thông đã cho thuộc trường hợp 1 (kết quả lấy độ chính xác ba
chữ số sau dấu chấm thập phân), còn ngược lại ghi số 0.
Ví dụ:
XDDUONG.INP XDDUONG.OUT
54 1
11 3.162
24
65
73
42
12
23
34
15

Bài 5: Nông trại


Một nông trại nuôi N con bò (1≤ N ≤ 100). Người ta xây dựng một khu vực nhốt
bò gồm N chuồng theo điều kiện đặc biệt như sau:
- Mỗi chuồng bò là một hình đa giác có từ 3 đến 8 cạnh;

24
- Mỗi bức ngăn của một chuồng là ranh giới giữa hai chuồng kề nhau, hoặc
giữa một chuồng và vùng diện tích bên ngoài;
- Mỗi chuồng chỉ nhốt một con bò và lúc ban đầu không có con bò nào ở bên
ngoài chuồng.
Tuy nhiên, lũ bò luôn có xu hướng tìm cách thoát ra khỏi chuồng và đến với
nhau trong cùng một chuồng nào đó hoặc cùng thoát ra ngoài khu chuồng. Mỗi
vách chuồng có độ kiên cố nhất định, và bọn bò muốn thoát khỏi chuồng thì chỉ
có cách húc vỡ vách ngăn nào đó. Biết rằng vách chuồng x cần sự nỗ lực C(x)
đề phá vỡ nó.
Yêu cầu: Với sơ đồ khu chuồng bò cho trước, em hãy cho biết bọn bò cần tổng
độ nỗ lực tối thiếu là bao nhiêu để có thể cùng ở một chỗ
với nhau (trong cùng một chuồng nào đó hoặc ở ngoài khu
chuồng).
Dữ liệu: Cho trong tệp FARM.INP, gồm:
- Dòng đầu chứa số nguyên N, là số chuồng bò.
- Dòng thứ i trong N dòng sau: mô tả thông tin về một
chuồng bò thứ i, gồm: đầu dòng là số nguyên M cho biết
chuồng bò có M góc (M cạnh, 3≤ M ≤8); M số nguyên
(có giá trị không quá 1000) theo sau là số hiệu của M góc chuồng bò được
cho theo thuận (hoặc nghịch) chiều kim đồng hồ; M số nguyên cuối dòng (có
giá trị không quá 5000) cho biết giá trị nỗ lực cần có để phá vách chuồng
tương ứng.
Kết quả: Ghi ra tệp FARM.OUT số nguyên duy nhất là tổng sự nỗ lực tối thiểu
để lũ bò đến ở chung với nhau trong một chuồng nào đó hoặc cùng thoát ra khỏi
khu chuồng.
Ví dụ: (ứng với hình vẽ trên, các số trong vòng tròn là số hiệu góc của chuồng,
các số in nghiêng chỉ giá trị nỗ lực để phá các vách chuồng tương ứng)

FARM.INP FARM.OUT
4 10
3123746
412457726
447654892
53247847477
(Giải thích, với sơ đồ khu chuồng như hình vẽ, lũ bò cần phá vách (2,3), (4,5),
(4,7) và tổng nỗ lực là 10.)

V. Kết luận
Trên đây là những trình bày của tôi về một số công thức và phương pháp
giải các bài toán hình học trong tin học. Do thời gian có hạn nên việc trình bày
có thể chưa được đầy đủ và các thuật toán có thể chưa tối ưu. Rất mong nhận
được sự đóng góp từ các bạn!
25

You might also like