Professional Documents
Culture Documents
Cac Thua Toan Hinh Hoc
Cac Thua Toan Hinh Hoc
I. Mở đầu .......................................................................................................... 3
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.
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
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;
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);
close(input);
close(output);
end.
Test:
https://www.mediafire.com/file/jwypuuv1uji53n3/CD+HH2.rar
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.
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;
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;
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 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 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
22
Ví dụ:
VECTO.INP VECTO.OUT
5 202
5 -8
-4 2
4 -2
21
-6 4
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
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