Rõ ràng ta có thể đưa bài toán về giống dạng đồ thị cuả Bài toán 1 , với N ngân hàng là N đỉnh của đồ thị, một đỉnh được nối tới một đỉnh duy nhất là chi nhánh mà nó giữ HTDL. Vậy ta th[r]
(1)Các tốn với chu trình đồ thị
Nguyễn Văn Trung ứng dụng chu trình đồ thị chưa xét nhiều Tôi xin đưa số bài toán tương đối hay, khó sử dụng tính chất chu trình mà đem lại cài đặt đơn giản so với thuật giải khác có phần hiệu nhiều
Định nghĩa: Trong đồ thị có hướng, độ dài chu trình đồ thị số đỉnh thuộc chu trình
Bài toán 1: Đồng hồ cổ
Trong ngơi mộ cổ, nhà khoa học tìm thấy thiết bị đặc biệt, có N nút, nút khắc kí tự đặc biệt, ấn vào nút đặc biệt đế, thiết bị bắt đầu hoạt động! Sau ngày, vị trí kí tự lại thay đổi Kết quan sát cho thấy, sau khoảng thời gian cố định, kí tự vị trí i chuyển tới vị trí j i có j cố định Dĩ nhiên, vị trí có ký tự Ngừơi ta dự đoán lịch đếm ngày phục vụ cho cơng việc đó, ví dụ để xác định thời gian luyện dưựơc liệu điều chế biệt dựơc bí ẩn tính tuổi người Số khoảng thời gian từ bấm nút khởi động đến lúc tất ký tự quay trở vị trí ban đầu đưựơc gọi chu kỳ hoạt động, hay ngắn gọn chu kỳ Hãy lập trình xác định chu kỳ thiết bị tìm thấy, biết khơng có chu kỳ vựơt q 1012
Dữ liệu: vào từ file văn CLOCK.INP:
- Dòng chứa số nguyên N - số ký tự thiết bị (1 <= n <= 10000)
- Các dòng sau chứa N số nguyên A1, A2, , An cho biết ký tự vị trí i chuyển tới vị trí Ai sau khoảng thời gian Các số cách dấu cách
Kết quả: Ra file văn CLOCK.OUT số nguyên - chu kỳ thiết bị
Ví dụ:
Thuật tốn :
1 Đánh số ký tự từ đến N tương ứng với N đỉnh đồ thị
2 Sau khoảng thời gian kí tự i chuyển tới vị trí A[i], ta coi đỉnh thứ i đồ thị có cạnh nối đến đỉnh A[i], ta đồ thị có hướng gồm N đỉnh, cung nối từ kí tự đến kí tự biến đổi thành
3 Gọi t[i] = thời gian để kí tự thứ i chuyển vị trí ban đầu Xuất phát từ đỉnh i đồ thị dễ nhận thấy từ i có đường để quay trở i, đường chu trình chứa i t[i] = độ dài chu trình chứa đỉnh i
4 Dùng mảng đánh dấu free[i] = true chưa tìm chu trình chứa đỉnh i, false ngược lại
Đoạn mã sau tìm t[i] với i = 1…n;
(2)var
i, j, nt : Integer; {nt biến cho biết độ dài chu trình xét}
begin
FillChar(Free, SizeOf(Free), True);
for i := to n do if Free[i] then begin
nt := 0; j := i; {bắt đầu tìm chu trình xuất phát từ đỉnh i}
repeat
Free[j] := False; Inc(nt);
j := A[j];
until j = i;
repeat
t[j] := nt; {các đỉnh thuộc chu trình có thời gian biến đổi nt } j := A[j];
until j = i;
end ;
end ;
Ta xét tiếp toán : Như ta biết sau khoảng thời gian t[i] ký tự vị trí i vị trí ban đầu, hiển nhiên sau khoảng thời gian BS(t[i]) { bội số t[i]} i trở vị trí ban đầu Vậy thời gian nhỏ để ký tự trở vị trí ban đầu BCNN(t[i]) i = n Bài tốn đưa tốn tìm BCNN số cho trước Đến vấn đề giải ổn thoả, ta bỏ qua bước cịn lại tốn
Bài tốn 2: Tập cực đại:
Cho n ( n ≤ 20000) đánh số hiệu từ đến N Trên ghi số nguyên F[i], (1 ≤ F[i] ≤ n, i = n), có nhiều ghi số Hãy chọn n tập nhiều cho tập hợp số hiệu đựơc chọn giống hệt với tập hợp số ghi
Input: file văn CARD.INP: - Dòng đầu ghi số n
- Dòng gồm n số, số thứ i số ghi thứ i Output: file văn CARD.OUT:
- Dòng đầu ghi số k, số lớn đựơc chọn
- Dòng ghi k số số hiệu đựơc chọn theo thứ tự tăng dần Ví dụ:
(3)1 Dùng mảng O[1 n] O[i] = số ghi thứ i, i = n
2 Mảng đánh dấu free[1 n] free[i] = false i thuộc chu trình đó, free[i] = true i khơng thuộc chu trình nào, i = n
3 Ta coi n n đỉnh đồ thị có hướng, cung nối từ đỉnh thứ i đến đỉnh ghi thứ i tức O[i], có n cung tất Một tập thoả mãn đề tập có số hiệu trùng với số hiệu số ghi chứng tỏ với đỉnh u thuộc tập chọn ra, u phải thuộc chu trình Thật vậy:
Giả sử V = {x 1 , x 2 ,…, x t } tập chọn ra.Ta có xu thuộc đồ thị có cung ra, giả sử x u có cung đến x u1 , x u1 có cung đến x u2 , … cuối x ui có
cung đến x u x u thuộc tập chọn
Từ suy đỉnh thuộc chu trình Xét ví dụ trên, tập chọn
1 10
Mảng O: 10 Chu trình 1: →5 → → →1 Chu trình 2: → →
Chu trình 3: 4→ →7 →4 Chu trình 4: 10 → 10
Như vậy, chu trình cho ta tập thoả mãn số hiệu chúng trùng với tập số ghi Vậy đồ thị này, ta cứu tìm hết chu trình đưựơc tập cực đại Như chứng minh, dễ thấy đỉnh u thuộc chu trình hiển nhiên q trình tìm chu trình kết thúc sau khơng q n lần lặp Với đỉnh khơng thuộc chu trình nào, ta lặp việc tìm kiếm, số lần tìm kiếm vượt n đỉnh nối với đỉnh khác thuộc chu trình ta kết luận đỉnh khơng thuộc chu trình
Chương trình đựơc cài đặt theo mơ hình sau: {Khai báo }
{Đọc liệu } procedure Solve; var
i, j, Loop : Integer; begin
FillChar(Free, SizeOf(Free), True); for i := to n
if Free[i] then {nếu đỉnh i chưa thuộc chu trình nào} begin
j := i; Loop := 0; repeat
j := O[j]; Inc(Loop);
if Loop > n then Break; {nếu số lần lặp vượt n} until (j = i) or (not Free[j]) or False;
(4)repeat
j := O[j]; Free[j] := False; {đi theo chu trình vừa tìm để ghi nhận} until j = i;
end; end;
{Ghi kết quả}
Việc kiểm tra tính đắn tốn dễ, bạn đọc tự viết chương trình kiểm tra
Bài toán 3: Hệ thống liệu Ngân Hàng
Một ngân hàng có N chi nhánh có tên từ đến N, chi nhánh có hệ thống liệu (HTDL), hai chi nhánh khác có HTDL khác Trong lần thay đổi máy tính tồn N chi nhánh, sơ xuất, ngừơi ta cài đặt không vị trí HTDL, chẳng hạn HTDL A chi nhánh B, B chi nhánh C, …(có thể có chi nhánh giữ HTDL nó), hai chi nhánh khác giữ hai HTDL khác
Cần phải tiến hành tráo đổi HTDL chi nhánh cho chi nhánh có đựơc HTDL Giữa hai chi nhánh tiến hành trao đổi ngày, hai cặp máy khác làm đồn thời công việc ngày Hãy tính xem cần ngày để hồn tất cơng việc
Input: file văn NH.INP - Dòng đầu ghi số N <= 10000
- Dòng thứ hai ghi N số, số thứ i HTDL chi nhánh mà chi nhánh i giữ thời
Output: file văn NH.OUT
- Dòng đầu ghi số M số ngày cần cho việc tráo đổi
- M dòng tiếp theo, dòng gồm n số, số thứ i n số số hiệu chi nhánh tiến hành tráo đổi với chi nhánh thứ i ngày
Ví dụ :
Hướng dẫn:
Bài tương đối khó giải theo cách giải thơng thường Tuy nhiên điều đặc biệt toán liệu lớn, N ≤ 10000 thời gian để tráo đổi lại không lớn, nhiều hai ngày
Rõ ràng ta đưa tốn giống dạng đồ thị cuả Bài toán 1, với N ngân hàng N đỉnh đồ thị, đỉnh nối tới đỉnh chi nhánh mà giữ HTDL Vậy ta thử làm trên, tìm chu trình thử xem có điều đặc biệt
Xét ví dụ trên, chu trình tìm là: → → → (→1) Chúng ta dễ dàng nhận thấy đổi chỗ 3, HTDL chi nhánh lúc là:
1
(5)và cần lần đổi chỗ ngày hai xong Đến đây, ta có tính chất sau chu trình này:
x → x → x →… → x p − → x p − → x p → x Nếu ta đổi chỗ x với x p, x với x p − 1, x với x p − 2, …, x i với x p + − i lúc chi nhánh tồn hai khả năng: - Hoặc có hệ thống liệu
- Nếu khơng, giữ HTDL chi nhánh k chi nhánh giữ HTDL
Tóm lại ta có cách đổi chỗ sau:
- Ngày 1: Với chi nhánh i, ta tìm chu trình chứa tiến hành tráo đổi : Gọi nCir độ dài chu trình (x 1, x , …, x nCir) với x1 = i, ta đổi chỗ HTDL chi nhánh x j với chi nhánh x nCir + … j, j = nCir
- Ngày 2: Sau tráo đổi HTDL ngân hàng cho nhau, giả sử ngân hàng thứ i giữ HTDL k ta việc ghi HTDL mà chi nhánh k giữ ban đầu, ta phải dùng mảng để ghi lại liệu ta đọc vào, chương trình mảng Giu }
Tồn chương trình giải sau:
Program HeThongDuLieuCuaNganHang; Const
InputFile = ’NH.INP’; OutputFile = ’NH.OUT’; nMax = 10000;
Type
InterArr = array [1 nMax] of Integer; Var
Fi, Fo : Text;
Giu, C : InterArr; { Giu[i] HTDL mà chi nhánh i giữ }
D : ^InterArr; { D[i]= ngân hàng đổi HTDL cho ngân hàng i ngày 1}
Free: array [1 nMax] of BooLean; { Mảng đánh dấu xem đỉnh xét chưa } n : Integer;
procedure Init; begin
New(D);
FillChar(Free, SizeOf(Free), True); end;
procedure Enter; var
i : Integer; begin
Assign(Fi, InputFile); Reset(Fi); ReadLn(Fi, n);
(6)Close(Fi); end;
procedure Swap(x, y : Integer); { đổi chỗ HTDL chi nhánh x y giữ cho nhau }
begin
D^[x] := y; D^[y] := x; end;
procedure Solve; var
i, j, Count, L, H : Integer; begin
D^ := Giu; {Mảng D đựơc khởi tạo giá trị mảng Giu } for i := to n
if Free[i] then { xét ngân hàng chua thuộc chu trình } begin
Count := 0; j := i; {Count độ dài chu trình} repeat
Free[j] := False;
Inc(Count); C[Count] := j; j := Giu[j];
until j = i;
{Đổi chỗ ngày 1} L := 1; H := Count; while L <= H begin
Swap(C[L], C[H]);
{ Đổi chỗ HTDL ngân hàng C[L] C[H], với L + H = N + } Inc(L); Dec(H);
end; end; end;
procedure Result; var
i : Integer; begin
Assign(Fo, OutputFile); Rewrite(Fo); WriteLn(Fo, 2);
for i := to n Write(Fo, D^[i] : 6); {Đây kết đổi chỗ ngày 1} WriteLn(Fo);
for i := to n Write(Fo, Giu[D^[i]] : 6);
{Ngày việc ghi HTDL mà chi nhánh i giữ trứơc tráo đổi} Close(Fo);
(7)