1. Trang chủ
  2. » Công Nghệ Thông Tin

Bài toán chu trình đồ thị

7 615 6
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Bài Toán Chu Trình Đồ Thị
Tác giả Nguyễn Văn Trung
Trường học Trường Đại Học
Thể loại Bài Tập
Định dạng
Số trang 7
Dung lượng 66 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Bài toán chu trình đồ thị

Trang 1

Các bài toán với chu trình đồ thị

Nguyễn Văn Trung

ứng dụng của chu trình trong đồ thị hiện chưa được xét nhiều Tôi xin đưa ra một số các bài toán tương đối hay, khó sử dụng tính chất của chu trình mà đem lại cài đặt hết sức đơn giản so với các thuật giải khác và có phần hiệu quả hơn rất nhiều

Định nghĩa: Trong một đồ thị có hướng, độ dài của một chu trình của đồ thị là số đỉnh

thuộc chu trình đó

Bài toán 1: Đồng hồ cổ

Trong một ngôi mộ cổ, các nhà khoa học đã tìm thấy một thiết bị đặc biệt, trên đó có N nút, mỗi nút khắc một kí tự đặc biệt, ấn vào nút đặc biệt dưới đế, thiết bị bắt đầu hoạt động! Sau mỗi ngày, vị trí các kí tự lại thay đổi Kết quả quan sát cho thấy, sau mỗi

khoảng thời gian cố định, kí tự ở vị trí i sẽ chuyển tới vị trí j và mỗi i có một j cố định của mình Dĩ nhiên, ở mỗi vị trí chỉ có một ký tự Ngừơi ta dự đoán đây là lịch đếm ngày phục

vụ cho một công việc nào đó, ví dụ để xác định thời gian luyện dưựơc liệu điều chế các biệt dựơc bí ẩn hoặc tính tuổi của một người Số khoảng thời gian từ khi bấm nút khởi động đến lúc tất cả các ký tự quay trở về vị trí ban đầu của nó đưựơc gọi là chu kỳ của hoạt động, hay ngắn gọn là chu kỳ Hãy lập trình xác định chu kỳ của thiết bị tìm thấy, biết rằng không có chu kỳ nào vựơt quá 1012

Dữ liệu: vào từ file văn bản CLOCK.INP:

- Dòng đầu tiên chứa số nguyên N - số ký tự trên 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 sẽ chuyển tới vị trí

Ai sau một khoảng thời gian Các số cách nhau ít nhất một dấu cách

Kết quả: Ra file văn bản CLOCK.OUT số nguyên - chu kỳ của thiết bị

Ví dụ:

Thuật toán :

1 Đánh số các ký tự từ 1 đến N tương ứng với N đỉnh của đồ thị

2 Sau một khoảng thời gian kí tự i sẽ chuyển tới vị trí A[i], ta coi như đỉnh thứ i của đồ thị

có cạnh nối đến đỉnh A[i], ta được một đồ thị có hướng gồm N đỉnh, một cung sẽ nối từ kí

tự đến kí tự tiếp theo nó biến đổi thành

3 Gọi t[i] = thời gian để kí tự thứ i chuyển về vị trí ban đầu Xuất phát từ đỉnh i của đồ thị

dễ nhận thấy từ i chỉ có một đường đi duy nhất để quay trở về i, đường đi đó chính là chu trình chứa i do đó t[i] = độ dài chu trình chứa đỉnh i

4 Dùng mảng đánh dấu free[i] = true nếu chưa tìm được chu trình chứa đỉnh i, false nếu ngược lại

Đoạn mã sau tìm t[i] với i = 1…n;

procedure findt;

Trang 2

var

i, j, nt : Integer; {nt là biến cho biết độ dài của chu trình đang xét}

begin

FillChar(Free, SizeOf(Free), True);

for i := 1 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 đều có thời gian biến đổi là nt }

j := A[j];

until j = i;

end ;

end ;

Ta xét tiếp bài toán : Như vậy ta đã biết sau một khoảng thời gian t[i] thì ký tự ở vị trí i sẽ

về vị trí ban đầu, hiển nhiên sau một khoảng thời gian là BS(t[i]) { bội số của t[i]} thì i

cũng sẽ trở về vị trí ban đầu Vậy thời gian nhỏ nhất để các ký tự đều trở về vị trí ban đầu

là BCNN(t[i]) i = 1 n Bài toán đưa về bài toán cơ bản là tìm BCNN của các số cho trước Đến đây vấn đề đã được giải quyết ổn thoả, ta có thể bỏ qua bước còn lại của bài toán

Bài toán 2: Tập các lá bài cực đại:

Cho n lá bài ( n ≤ 20000) được đánh số hiệu từ 1 đến N Trên mỗi lá bài ghi một số nguyên

F[i], (1 ≤ F[i] ≤ n, i = 1 n), có thể có nhiều lá bài cùng ghi một số Hãy chọn ra trong n lá bài trên một tập nhiều nhất các lá bài sao cho tập hợp các số hiệu của các lá bài đựơc chọn giống hệt với tập hợp của các số ghi trên các lá bài

Input: file văn bản CARD.INP:

- Dòng đầu ghi số n

- Dòng tiếp theo gồm n số, số thứ i là số ghi trên lá bài thứ i

Output: file văn bản CARD.OUT:

- Dòng đầu ghi số k, là số lớn nhất các lá bài đựơc chọn

- Dòng tiếp theo ghi k số là số hiệu của các lá bài đựơc chọn theo thứ tự tăng dần

Ví dụ:

Hướng dẫn:

1 Dùng mảng O[1 n] trong đó O[i] = số ghi trên lá bài thứ i, i = 1 n

Trang 3

2 Mảng đánh dấu free[1 n] trong đó free[i] = false nếu i thuộc một chu trình nào đó, free[i] = true nếu i không thuộc một chu trình nào, i = 1 n

3 Ta coi n là bài trên là n đỉnh của một đồ thị có hướng, một cung nối từ đỉnh thứ i đến đỉnh ghi trên lá bài thứ i tức là O[i], như vậy có n cung tất cả Một tập các lá bài thoả mãn

đề bài là tập có số hiệu các lá bài trùng với số hiệu các số ghi trên lá bài chứng tỏ với một đỉnh u thuộc tập được chọn ra, u phải thuộc một chu trình nào đó Thật vậy:

Giả sử V = {x 1 , x 2 ,…, x t } là tập các lá bài được chọn ra.Ta có xu thuộc đồ thị có duy nhất một cung đi ra, giả sử x u có cung đến x u1 , x u1 có cung đến x u2 , … cuối cùng x ui có cung đến x u vì x u thuộc tập được chọn

Từ đây cũng suy ra mỗi đỉnh chỉ thuộc một chu trình duy nhất

Xét ví dụ trên, tập chọn ra là

1 2 3 4 5 6 7 8 9 10

Mảng O: 5 6 8 9 3 2 4 1 7 10

Chu trình 1: 1 →5 → 3 → 8 →1

Chu trình 2: 2 → 6 → 2

Chu trình 3: 4→ 9 →7 →4

Chu trình 4: 10 → 10

Như vậy, mỗi chu trình sẽ cho ta các tập lá bài thoả mãn số hiệu của chúng trùng với tập các số ghi trên các lá bài Vậy trong đồ thị này, ta cứu tìm hết các chu trình thì sẽ đưựơc tập các lá bài cực đại Như trên đã chứng minh, dễ thấy nếu đỉnh u thuộc một chu trình nào

đó thì hiển nhiên quá trình tìm chu trình sẽ kết thúc sau không quá n lần lặp Với một đỉnh không thuộc chu trình nào, ta lặp việc tìm kiếm, nếu số lần tìm kiếm vượt quá n hoặc đỉnh

đó nối với một đỉnh khác đã thuộc chu trình thì ta kết luận đỉnh đó không thuộc chu trình nào

Chương trình đựơc cài đặt theo mô hình sau:

{Khai báo }

{Đọc dữ liệu }

procedure Solve;

var

i, j, Loop : Integer;

begin

FillChar(Free, SizeOf(Free), True);

for i := 1 to n do

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 quá n}

until (j = i) or (not Free[j]) or False;

if j = i then {nếu tìm được chu trình chứa i}

repeat

Trang 4

j := O[j]; Free[j] := False; {đi theo chu trình vừa tìm được để ghi nhận}

until j = i;

end;

end;

{Ghi kết quả}

Việc kiểm tra tính đúng đắn của bài toán rất dễ, bạn đọc có thể tự viết chương trình kiểm tra

Bài toán 3: Hệ thống dữ liệu của Ngân Hàng

Một ngân hàng có N chi nhánh có tên từ 1 đến N, mỗi chi nhánh có một hệ thống dữ liệu (HTDL), hai chi nhánh khác nhau có HTDL khác nhau Trong một lần thay đổi máy tính của toàn bộ N chi nhánh, do sơ xuất, ngừơi ta đã cài đặt không đúng vị trí của các HTDL, chẳng hạn HTDL tại A là của chi nhánh B, tại B là của chi nhánh C, …(có thể có chi nhánh

đã giữ đúng HTDL của nó), mặc dù hai chi nhánh khác nhau vẫn giữ hai HTDL khác nhau

Cần phải tiến hành tráo đổi các HTDL giữa các chi nhánh sao cho mỗi chi nhánh có đựơc HTDL của nó Giữa hai chi nhánh có thể tiến hành trao đổi trong một ngày, hai cặp máy khác nhau có thể làm đồn thời công việc này trong cùng một ngày Hãy tính xem cần ít nhất bao nhiêu ngày để hoàn tất công việc này

Input: file văn bản NH.INP

- Dòng đầu ghi số N <= 10000

- Dòng thứ hai ghi N số, số thứ i là HTDL của chi nhánh mà chi nhánh i đang giữ hiện thời

Output: file văn bản NH.OUT

- Dòng đầu ghi số M là số ngày ít nhất cần cho việc tráo đổi

- M dòng tiếp theo, mỗi dòng gồm n số, số thứ i trong n số đó là số hiệu chi nhánh tiến hành tráo đổi với chi nhánh thứ i trong ngày đó

Ví dụ :

Hướng dẫn:

Bài này là một bài tương đối khó nếu giải theo cách giải thông thường Tuy nhiên điều đặc biệt ở bài toán này là tuy dữ liệu khá lớn, N ≤ 10000 nhưng thời gian để tráo đổi lại không lớn, nhiều nhất là hai ngày

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ử làm như các bài trên, tìm chu trình và thử xem có điều gì đặc biệt

Xét ví dụ trên, chu trình tìm được là: 1 → 4 → 2 → 3 (→1) Chúng ta có thể dễ dàng nhận thấy nếu đổi chỗ 1 và 3, 2 và 4 thì HTDL tại các chi nhánh lúc này là:

1 2 3 4

HTDL: 1 2 4 3

và chỉ cần một lần đổi chỗ 3 và 4 trong ngày hai là xong

Trang 5

Đến đây, ta có tính chất sau của chu trình trong bài này:

x 1 → x 2 → x 3 →… → x p − 2 → x p − 1 → x p → x 1 Nếu ta đổi chỗ x 1 với x p, x 2 với x p − 1,

x 3 với x p − 2 , …, x i với x p + 1 − i thì lúc này tại một chi nhánh sẽ tồn tại hai khả năng:

- Hoặc nó đã có hệ thống dữ liệu của chính nó

- Nếu không, nó giữ HTDL của chi nhánh k nào đó và chi nhánh này giữ HTDL của nó

Tóm lại ta có cách đổi chỗ như sau:

- Ngày 1: Với chi nhánh i, ta tìm chu trình chứa nó và tiến hành tráo đổi như trên : Gọi nCir là độ dài của chu trình (x 1, x 2 , …, x nCir) với x1 = i, ta cứ đổi chỗ HTDL của chi nhánh x j với chi nhánh x nCir + 1 … j, j = 1 nCir

- Ngày 2: Sau khi tráo đổi các HTDL của các ngân hàng cho nhau, giả sử ngân hàng thứ i đang giữ HTDL là k thì ta chỉ việc ghi ra HTDL mà chi nhánh k giữ ban đầu, do đó ta phải dùng một mảng để ghi lại dữ liệu ta đọc vào, ở chương trình của chúng ta là mảng Giu }

Toàn bộ chương trình giải như 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] là HTDL mà chi nhánh i giữ }

D : ^InterArr; { D[i]= ngân hàng đổi HTDL cho ngân hàng i trong ngày 1}

Free: array [1 nMax] of BooLean; { Mảng này đánh dấu xem một đỉ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);

for i := 1 to N do

Read(Fi, Giu[i]);

Close(Fi);

end;

Trang 6

procedure Swap(x, y : Integer); { đổi chỗ HTDL của chi nhánh x và y đang 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 là giá trị của mảng Giu }

for i := 1 to n do

if Free[i] then { xét những ngân hàng chua thuộc chu trình nào }

begin

Count := 0; j := i; {Count là độ dài của 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 do

begin

Swap(C[L], C[H]);

{ Đổi chỗ HTDL ngân hàng C[L] và C[H], với L + H = N + 1 }

Inc(L); Dec(H);

end;

end;

end;

procedure Result;

var

i : Integer;

begin

Assign(Fo, OutputFile); Rewrite(Fo);

WriteLn(Fo, 2);

for i := 1 to n do Write(Fo, D^[i] : 6); {Đây là kết quả của đổi chỗ ngày 1}

WriteLn(Fo);

for i := 1 to n do Write(Fo, Giu[D^[i]] : 6);

{Ngày 2 chỉ việc ghi ra HTDL mà chi nhánh tại i giữ trứơc khi tráo đổi}

Close(Fo);

end;

Begin

Init;

Trang 7

Enter; Solve; Result; End

Ngày đăng: 07/09/2012, 10:30

TỪ KHÓA LIÊN QUAN

w