Thuật toán tìm kiếm nhị phân và ứng dụng Tài liệu giới thiệu về thuật toán tìm kiếm nhị phân và một số biến thể Áp dụng giải một số bài tập dành cho học sinh giỏi Ngôn ngữ minh họa là ngôn ngữ Pascal
MỘT SỐ BÀI TẬP ỨNG DỤNG THUẬT TỐN TÌM KIẾM NHỊ PHÂN ĐỂ GIẢI Bài tập 1: Dãy tổng đối xứng Một dãy số nguyên không âm A[1], A[2], …, A[N] gọi dãy tổng đối xứng ta tách dãy làm dãy có tổng giá trị Nghĩa tồn số k đoạn [1 N] cho tổng A[1] + A[2] + + A[k] = A[k+1] + A[k+2] + + A[N] * Yêu cầu: Cho dãy gồm N số ngun khơng âm Hãy tìm dãy gồm phần tử liên tiếp dài mà dãy tổng đối xứng * Dữ liệu vào: Từ tệp văn BAI1.INP có cấu trúc: - Dịng chứa số nguyên N (2 ≤ N≤5000) - N dòng tiếp theo, dòng thứ i N dòng chứa giá trị phần tử A[i] dãy ( i = 1, 2, …, N) Các số dãy không âm nhỏ 200000 * Dữ liệu ra: Ghi vào tệp văn BAI1.OUT có cấu trúc: Gồm dòng: Ghi số độ dài lớn dãy gồm phần tử liên tiếp dài dãy tổng đối xứng Nếu khơng có kết ghi số Ví dụ: 10 BAI1.INP BAI1.OUT * Ý tưởng Gọi B[i] = A[1]+A[2]+…+A[i] Với i = 1, 2, , N Cần tìm đoạn dài dãy tổng đối xứng - Ta có tổng phần tử từ vị trí L đến R bằng: B[R]-B[L-1] - Xét đoạn [L,R], LR, ta tìm vị trí K cho tổng phần tử đoạn [L,K] tổng phần từ đoạn [K+1,R], có K [L,R] dãy tổng đối xứng Nghĩa tìm K để: BK BL 1 BR BK BK BR BL 1 Nhận xét 1: Nếu BR BL 1 chẵn ta tìm vị trí K Nhận xét 2: Tìm vị trí K cho BK BR BL 1 Vì B tăng, nghĩa BL 1 BL BR , nên dùng thuật toán tìm kiếm nhị phân tìm vị trí K nhanh thỏa liệu vào toán Thuật toán kiểm tra đoạn [L, R] dãy tổng đối xứng không Đặt gt BR BL 1 function np(gt,L,R:longint):boolean; var g:longint; begin while (Lb[g] then L:=g+1 else R:=g-1; end; exit(false); end; Nhận xét 3: Nếu độ dài dãy cần tìm có, độ dài lớn Gọi d độ dài cần tìm, d n, xét đoạn có độ dài d kiểm tra đoạn có dãy tổng đối xứng khơng Bài tốn cần tìm độ dài lớn nên ta duyệt đoạn có độ dài d từ N xuống + Với đoạn, ta xác định vị trí đầu đoạn L vị trí cuối đoạn R Kiểm tra đoạn có dãy tổng đối xứng khơng thuật tốn tìm kiếm nhị phân Nếu thỏa d kết cần tìm dừng * Cài đặt chương trình CONST FI='BAI1.INP'; FO='BAI1.OUT'; VAR a, B:array[0 maxn] of longint; n,dodaimax:longint; MAXN=5000; L,R:longint; f:text; procedure doc; var i:longint; begin assign(f,fi); reset(f); readln(f,n); for i:=1 to n readln(f,a[i]); fillchar(b,sizeof(b),0); for i:=1 to n b[i]:=b[i-1]+a[i]; close(f); dodaimax:=0; end; function np(gt,L,R:longint):boolean; var g:longint; begin while (Lb[g] then L:=g+1 else R:=g-1; end; exit(false); end; procedure xuly; var d,gt:longint; begin for d:=n downto for L:=1 to n-d+1 begin R:=L+d-1; if (B[L-1]+b[R]) mod = then begin gt:=(b[l-1]+b[r]) div 2; if np(gt,L,R) then begin dodaimax:=d; exit; end; end; end; end; procedure ghi; var i:longint; begin assign(f,fo); rewrite(f); end; begin end writeln(f,dodaimax); close(f); doc; xuly; ghi; Bài Bước nhảy xa Cho dãy A gồm N số nguyên không âm A1, A2,…, AN Một bước nhảy từ phần tử Ai đến phần tử Aj gọi bước nhảy xa dãy thỏa mãn điều kiện sau: ≤ i < j ≤ N Aj – Ai ≥ P j – i lớn Khi j – i gọi độ dài bước nhảy xa dãy Yêu cầu: Tìm độ dài bước nhảy xa dãy A Dữ liệu vào : Từ tệp BAI2.INP có cấu trúc sau: - Dòng 1: Gồm hai số nguyên N P (1 ≤ N ≤ 105; ≤ P ≤ 109) - Dòng 2: Gồm N số nguyên A1, A2,…, AN (0 ≤ Ai ≤ 109 với ≤ i ≤ N) ( Các số cách dấu cách ) Dữ liệu : Ghi vào tệp BAI2.OUT gồm số nguyên dương độ dài bước nhảy xa dãy (Nếu khơng có bước nhảy thỏa mãn ghi kết 0) Ví dụ: BAI2.INP BAI2.OUT 3 * Ý tưởng Gọi L[i] giá trị nhỏ dãy A từ phần tử thứ đến phần tử thứ i L[1]:=A[1]; L[i]:=Min(L[i-1],A[i]), i=2,3, , N i Ai Li 4 Với vị trí j = 2, 3 2 , n ta tìm vị trí i [1, j-1] nhỏ cho a[j]- PL[i] Khi ta đoạn j-i thỏa mãn điều kiện toán, so sánh độ dài đoạn [i,j] với phương án tối ưu Vì ≤ N ≤ 105 , Mảng L mảng không tăng nên ta áp dụng thuật tốn tìm kiếm nhị phân đoạn [1, j-1] để tìm vị trí i nhỏ cho a[j]-PL[i] Độ phức tạp O(NlogN) * Cài đặt chương trình CONST FI='BAI2.inp'; fo='BAI2.out'; maxn=100000; var a,L:array[1 maxn] of longint; n,p,kq:longint; f:text; procedure doc;var i:longint; begin assign(f,fi); reset(f); readln(f,n,p); for i:=1 to n read(f,a[i]); close(f); end; function min(a,b:longint):longint; begin if akq then kq:=j-i; end; end; procedure ghi; begin assign(f,fo); rewrite(f); writeln(f,kq); close(f); end; begin doc; xuly; ghi; end Bài Chăn bò Bờm nhận vào làm việc cho nhà Phú Ông nhiệm vụ cậu ta chăn bị Với tính ham chơi nên cậu ta định đóng N cọc cột bị vào đó, cậu ta thỏa thích chơi đùa mà khơng sợ bị N cọc đặt đường thẳng vị trí x1, x2, …, xn Phú Ơng giao cho Bờm chăn thả C bò Những bò khơng thích bị buộc vào cọc gần bò khác Chúng trở nên bị buộc gần nhau, chúng cho bị tranh giành cỏ Để tránh việc bò làm đau nhau, Bờm muốn buộc bò vào cọc, cho khoảng cách nhỏ hai bị lớn u cầu: Hãy tìm giá trị lớn Dữ liệu vào: Cho tệp BAI3.INP gồm: Dòng 1: Chứa hai số nguyên dương N C (2 ≤ C ≤ N ≤ 10 5) N dòng tiếp theo, dòng thứ i chứa số nguyên xi mơ tả vị trí cọc (0 ≤ xi ≤ 109) Đương nhiên khơng có hai cọc vị trí Dữ liệu ra: Ghi vào tệp BAI3.OUT In giá trị lớn khoảng cách nhỏ hai bò Ví dụ: * Ý tưởng BAI3.INP BAI3.OUT Rất khó để xác định khoảng cách nhỏ hai bò lớn Ta nhận thấy kết tốn nằm đoạn từ đến (x max - xmin) Chính ta tìm kiếm nhị phân theo khoảng cách đoạn [1 x max - xmin] Với giá trị khoảng cách K, ta kiểm tra buộc bò với khoảng cách nhỏ lớn K có thỏa mãn khơng (buộc đủ C bị), thỏa ghi nhận kết thu hẹp khơng gian tìm kiếm đoạn [K+1 x max - xmin], khơng thỏa tìm đoạn [1 K-1] * Cài đặt chương trình const fi='BAI3.inp'; fo='BAI3.out'; maxn=100005; var f:text; a:array [1 maxn] of int64; n,c:longint; res:int64; ok:boolean; procedure nhap; var i:longint; begin assign(f,fi); reset(f); readln(f,n,c); for i:=1 to n readln(f,a[i]); close(f); end; procedure sort(l,r: longint); var i,j,x,y: longint; begin i:=l; j:=r; x:=a[(l+r) div 2]; repeat while a[i]j; if l