1. Trang chủ
  2. » Luận Văn - Báo Cáo

Bài tập môn học kỹ thuật lập trình

26 476 0

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

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 26
Dung lượng 889,38 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 tập môn học kỹ thuật lập trình

Trang 1

TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI

VIỆN CÔNG NGHỆ THÔNG TIN VÀ TRUYỀN THÔNG

Vũ Văn Hiệp-CNTT2 20101545Nguyễn Xuân Hòa-CNTT2 20101559Giáo viên :

Lương Mạnh Bá

HÀ NỘINgày 25 tháng 3 năm 2012

Trang 2

Mục lục

1.1 Cách giải đệ quy 3

1.1.1 Phân tích bài toán 3

1.1.2 Thuật toán 4

1.1.3 Listing chương trình 4

1.1.4 Kết quả 9

1.2 Cách giải không đệ quy 13

1.2.1 Phân tích 13

1.2.2 Listing chương trình 14

1.2.3 Kết quả 17

2 Bài toán tháp Hà Nội 19 2.1 Cách giải đệ quy 19

2.1.1 Phân tích 19

2.1.2 Thuật toán 19

2.1.3 Listing chương trình 19

2.1.4 Kết quả 21

2.2 Cách giải không đệ quy 22

2.2.1 Phân tích thuật toán 22

2.2.2 Thuật toán 23

2.2.3 List chương trình 23

2.2.4 Kết quả 24

Trang 3

Hình 1: Quân Hậu ở ô (3,2) chiếm cột thứ 2, đường chéo loại I thứ 5 và đường chéo loại IIthứ 1

Đề bài: Liệt kê tất cả các cách sắp xếp N quân hậu trên bàn cờ N x N sao cho chúngkhông ăn được nhau

1.1 Cách giải đệ quy

1.1.1 Phân tích bài toán

Đánh số cột và số dòng của bàn cờ từ 0 đến N − 1 Rõ ràng N quân Hậu được xếp trên

N hàng khác nhau Do đó ta chỉ cần tìm xem mỗi quân Hậu được xếp ở cột nào Gọi colsi

là chỉ số cột của quân Hậu ở hàng thứ i (i = 0− > N − 1), colsi có thể lấy giá trị từ 0 đến

N − 1 Ta sẽ tìm colsi cho i từ 1 đến N − 1 Giá trị colsi = j được coi là thoả mãn khi ô cờ(i, j) chưa bị quân Hậu nào trong số những quân Hậu đã tìm trước đó ăn Một quân Hậu cóthể ăn theo chiều ngang, dọc và theo hai đường chéo

Một quân Hậu trước đó ăn ô (i, j) theo chiều ngang có nghĩa là quân Hậu đó ở hàng

i, điều này là không thể xảy ra do cách làm của ta là mỗi hàng chỉ xếp đúng một quânHậu Quân Hậu ăn theo chiều dọc có nghĩa là quân Hậu đó có chỉ số cột là j Đối với haiđường chéo, ta thấy một đường có i + j = const, (0 ≤ i + j ≤ 2N − 2), đường kia có phươngtrình i − j = const, (1 − N ≤ i − j ≤ N − 1) Như vậy có tất cả 2N − 1 đường chéo loại I(i + j = const) và 2N − 1 đường chéo loại II (i − j = const) Khi xếp một quân Hậu vào ô(i, j) thì có nghĩa là cột j; đường chéo loại I thứ (i + j) và đường chéo loại II thứ (i − j) đã

bị chiếm, các quân Hậu sau này không được phép đặt vào vị trí có cùng cột, đường chéo vớinó

Để biểu diễn một cột, đường chéo đã bị chiếm ta có thể sử dụng 3 mảng bool ai, bi, ci thểhiện việc cột thứ i, đường chéo loại I thứ i, đường chéo loại II i đã bị chiếm hay chưa Dongôn ngữ C không hỗ trợ mảng có chỉ số âm nên ta có thể gán:

bool raw_c[2*N];

bool* c = raw_c + N;

3

Trang 4

Khi đó, c[i] == raw_c[i + N ] với −N ≤ i ≤ N − 1.

1.1.2 Thuật toán

Ta dùng thuật toán đệ quy quay lui với bài toán này Cụ thể gồm các bước như sau:

1 Khởi tạo a, b, c đều bằng true, có nghĩa là tất cả các cột vào đường chéo lúc đầu đềuchưa bị chiếm i = 0 là thứ tự quân Hậu đang xét, cũng là chỉ số hàng của nó

2 Thử các giá trị j, 0 ≤ j ≤ N − 1 là chỉ số cột của quân Hậu hiện tại (hàng i)

3 Nếu ô (i, j) chưa bị chiếm, tức aj == bi+j == ci−j == true thì chấp nhận giá trị

j (gán colsi = j) Đánh dấu các cột, đưòng chéo tương ứng với ô (i, j) đã bị chiếm(aj = bi+j = ci−j = f alse) Nếu i == N − 1, tức là đã tìm ra 1 kết quả thì in nó ra.Còn lại ta tìm colsi+1 Sau đó trả lại ô (i, j) (tức là gán aj = bi+j = ci−j = true)

4 Nếu ô (i, j) đã bị chiếm thì lại tiếp tục thử giá trị tiếp theo của j

5 Nếu không có khả năng nào thoả mãn thì quay lại bước trước đó để tìm giá trị tiếptheo của colsi−1

Có thể biểu diễn bằng mã giả như sau:

1 def find(i):

Trang 5

24 *b, // Đường chéo I, i + j = const

25 *raw_c, *c; // Đường chéo II, i - j = const

26

27 void init_state(int);

28 void free_state(int);

29

30 void find(int, int);

31

32 void print_result(int const*, int);

33 void print_result_board(int const*, int);

34 void (*print_func)(int const*, int) = print_result;

Trang 6

78 void init_state(int n) {

79 a = (bool*) malloc(sizeof(bool)*n);

80 b = (bool*) malloc(sizeof(bool)*2*n);

81 raw_c = (bool*) malloc(sizeof(bool)*2*n), c = raw_c + n;

87 for(int i = 0; i < n; ++i)

88 a[i] = b[i] = raw_c[i] = true;

89 for(int i = n; i < 2*n; ++i)

90 b[i] = raw_c[i] = true;

Trang 7

117 void print_result(int const* cols, int n) {

118 static int count = 0

119

120 printf("Kết quả thứ %d: \n ", ++count);

121

122 /* giá trị thứ i trong kết quả là chỉ số cột

123 * của quân hậu có chỉ số hàng là i */

140 void print_result_board(int const* cols, int n) {

141 static int count = 0

149 for(int i = n-1; i >= 0; i) {

150 for(int j = 0; j < cols[i]; ++j)

Trang 8

167 * Description: Tìm vị trị đặt quân hậu tại hàng thứ curr_row

168 * thoả mãn yêu cầu

169 * =============================================================

170 */

171 void find(int curr_row, int n) {

172 for(int curr_col = 0; curr_col < n; ++curr_col) {

173 if(a[curr_col] /* cột curr_col chưa bị chiếm */

174 /* đường chéo loại I curr_row + curr_col chưa bị chiếm */

175 && b[curr_row + curr_col]

176 /* đường chéo loại II curr_row - curr_col chưa bị chiếm */

177 && c[curr_row - curr_col])

179 cols[curr_row] = curr_col;

180

181 // Cập nhật trạng thái

182 a[curr_col] = b[curr_row + curr_col]

193 a[curr_col] = b[curr_row + curr_col]

Trang 9

1.1.4 Kết quả

Với N = 8, có tất cả 92 kết quả khác nhau (46 nếu tính 2 kết quả đối xứng nhau là 1)

9

Trang 12

Có thể in kết quả dạng bàn cờ trực quan hơn, thay vì các con số chỉ vị trí:

./queen_recursive 5 board

Nếu kết quả quá dài, có thể lưu vào file Ví dụ: với N = 12 (có 14200 kết quả)

./queen_recursive 12 board > qr12.txt

Trang 13

1.2 Cách giải không đệ quy

1.2.1 Phân tích

Theo thuật toán đệ quy:

1 def find(i):

10 a[j] = b[i+j] = c[i-j] = True

Giả sử cấu hình hiện tại đang là cols = (j0, j1, , ji−1) Ta thấy:

ˆ Việc lựa chọn giá trị ji trong khoảng từ 0 đến N-1 ở dòng (2) được bắt đầu ngay saukhi đã tìm được ji−1 trước đó Và tiếp tục sau khi đã kết thúc việc tìm kết quả ứng vớicấu hình cols = (j0, j1, , ji−1, ji− 1),

ˆ Dòng (5) được thực hiện ngay sau khi chấp nhận một ji,

ˆ Dòng (10) được thực hiện khi hoặc là đã in ra một kết quả, hoặc là việc chấp nhận j

ở dòng (4) không dẫn ra được một kết quả đúng

Do đó ta có thể khử đệ quy bằng phép lặp, dùng một biến i để lưu giá trị của hàng hiệntại Cho ji thử lần lượt các giá trị từ 0 đến N-1, nếu thoả mãn thì cập nhật trạng thái, tăng

i lên 1 và tiếp tục với i + 1 Cho đến khi i == N − 1 thì in kết quả và trả lại trạng thái.Nếu không còn ji nào thoả mãn thì giảm i đi 1 và trả lại trạng thái cũ Quá trình kết thúckhi i == 0 và không còn giá trị j0 nào thoả mãn nữa (đã hết cấu hình để thử) Ta có mã giảnhư sau:

8 # Neu o (i, j) chua bi chiem

9 if a[j] && b[i+j] && c[i-j]:

10 a[j] = b[i+j] = c[i-j] = False

Trang 14

18 # Tra lai trang thai cu

20 elif i > 0:

21 # Tro lai thu gia tri tiep theo cua cols[i-1]

23

24 # Tra lai trang thai cu

25 a[j] = b[i+j] = c[i-j] = True

23 void print_result(int const*, int);

24 void print_result_board(int const*, int);

25 void (*print_func)(int const*, int) = print_result;

Trang 15

67 void print_result(int const *cols, int n) {

68 static int count = 0

69

70 printf("Kết quả thứ %d: \n ", ++count);

71

72 /* giá trị thứ i trong kết quả là chỉ số cột

73 * của quân hậu có chỉ số hàng là i */

Trang 16

80 /* - end of function print_result - */

90 void print_result_board(int const *cols, int n) {

91 static int count = 0

99 for(int i = n-1; i >= 0; i) {

100 for(int j = 0; j < cols[i]; ++j)

122 bool b[2*n]; // Đường chéo 1, i + j = const

123 bool raw_c[2*n], *c= raw_c + n; // Đường chéo 2, i - j = const

Trang 17

128 a[i] = b[i] = raw_c[i] = true;

129 for(int i = n; i < 2*n; ++i)

130 b[i] = raw_c[i] = true;

137 if(a[curr_col] /* cột curr_col chưa bị chiếm */

138 /* đường chéo loại I curr_row + curr_col chưa bị chiếm */

139 && b[curr_row + curr_col]

140 /* đường chéo loại II curr_row - curr_col chưa bị chiếm */

141 && c[curr_row - curr_col])

143 // Cập nhật trạng thái

144 a[curr_col] = b[curr_row + curr_col]

153 a[curr_col] = b[curr_row + curr_col]

161 a[curr_col] = b[curr_row + curr_col]

Trang 19

2 Bài toán tháp Hà Nội

Đề bài: Có 3 cọc a, b, c Trên cọc a có một chồng gồm n cái đĩa đường kính giảm dần từdưới lên trên Cần phải chuyển chồng đĩa từ cọc a dang cọc c tuân thủ qui tắc: mỗi lần chỉchuyển một đĩa và chỉ được xếp đĩa có đường kính nhỏ hơn lên đĩa có đường kính lớn hơn.Trong quá trình chuyển được phép dùng cọc b làm cọc trung gian

2.1 Cách giải đệ quy

2.1.1 Phân tích

Với n = 1, ta chỉ việc chuyển đĩa từ cọc a sang cọc c

Với n = 2, ta có thể di chuyển 1 đĩa (cái nhỏ hơn) từ cọc a sang cọc b, chuyển đĩa còn lại ởcọc a sang cọc c và cuối cùng chuyển đĩa đã ở cọc b sang cọc c Cả ba bước di chuyển này đềhợp lệ

Lập luận tương tự như trên, ta có thể di chuyển n đĩa bất kỳ (n > 1) như sau:

1 Chuyển n − 1 đĩa từ cọc a sang cọc trung gian b,

2 Chuyển đĩa lớn nhất từ cọc a sang cọc đích c,

3 Chuyển n − 1 đĩa từ cọc b sang cọc c

Có thể chứng minh được việc di chuyển như trên mất 2n− 1 bước

2.1.2 Thuật toán

Cách làm như trên đưa bài toàn di chuyển n đĩa về bài toán di chuyển n − 1 đĩa, rồi n − 2đĩa, Rất thích hợp cho một thuật toán đệ quy:

1 # Ham thuc hien viec di chuyen

2 def move(n, source, dest, mid):

3 if n > 1

4 move(n-1, source, mid, dest)

5 move( 1, source, dest, mid)

6 move(n-1, mid, dest, source)

Trang 20

34 if(ndisk > MAX_DISK || ndisk == 0) {

35 fprintf(stderr, "Số đĩa không hợp lệ \n ");

49 * Description: Di chuyển ndisk đĩa từ cột src đến cột dst,

50 * có thể sử dụng cột trung gian mid

51 * =============================================================

52 */

53 void move (size_t ndisk, char src, char dst, char mid) {

54 static size_t count = 0;

Trang 22

2.2 Cách giải không đệ quy

2.2.1 Phân tích thuật toán

Với bài toán tháp Hà Nội có rất nhiều các phương án để thực hiện việc phá bỏ đệ quynhư sử dụng vòng lặp, sử dụng stack và một trong số đó là sử dụng cây nhi phân(binarytree)

Như vậy chúng ta có thể mã hóa bài toán thông qua biểu diễn nhị phân của số thứ tự dichuyển, trong đó các dãy 1 và các dãy 0 tượng trưng cho các dãy đĩa liền kề nhau trên cùngmột cọc, và mỗi khi chữ số nào thay đổi thì đĩa kế tiếp dời sang trái hay phải một cọc(haychuyển sang cọc đối diện).Quy tắc:

1 Dãy bit đọc từ trái qua phải, và mỗi bit có thể được sử dụng để xác định vị trí tươngứng

2 Bit có cùng giá trị nghĩa là đĩa ương ứng được xếp chồng lên đỉnh đĩa trước trên cungmột cọc

3 Bit có cùng giá trị khác trước nghĩa là đĩa tương ứng có vị trí bên trái hoặc phải.Xácđịnh bên trái hay phải theo quy tắc sau:

ˆ Giả thiết cọc đích nằm bên trái và cọc nguồn nằm bên phải

ˆ Cũng giả thiết:cọc phải được tính như là cọc trái của cọc trái và ngược lại

ˆ Giả sử n là số đĩa lớn nhất trên cùng một cọc.Nếu n chẵn , đĩa sẽ được đặt bên trái,nếu n lẻ đĩa được đặt ở cọc phải

Để dễ hiểu ta xét một ví dụ bài toán tháp Hà Nội với ndisk = 3

(3 cọc lần lượt là a, b, c) Với ndisk = 3:

1 Ba đĩa cọc A kí hiệu là (000)2

2 Chuyển đĩa 1 từ a sang c, kí hiệu (001)2 = 110

Chữ số 1 biểu thị đĩa trên cùng được chuyển

3 Chuyển đĩa 2 từ a sang b, kí hiệu (010)2

Chữ số 1 biểu thị đĩa thứ 3 đã được chuyển, ba chữ số 0, 1, 0 biểu thị ba đĩa ở ba cọckhác nhau

4 Chuyển đĩa 1 từ c sang b, kí hiệu (011)2 = (3)10

Chữ số 1 ở vị trí thứ nhất biểu thị đĩa số 1 đã được chuyển Ba chữ số 0, 1, 1 biểu thịhai đĩa nhỏ ở trên cùng cọc c (đĩa thứ hai chồng lên đĩa thứ nhất), đĩa lớn ở cọc khác(cọc a) Cọc c trống

5 Chuyển đĩa số 3 từ cọc a sang cọc c, kí hiệu là (100)2

Chữ số 1 thứ ba biểu thị đĩa số 3 đã được chuyển Ba chữ số 1, 0, 0 biểu thị hai đĩanhỏ ở trên cùng một cọc b (đĩa thứ hai chồng lên đĩa thứ nhất) và không chuyển, đĩalớn ở cọc khác (cọc c) Cọc a trống

Trang 23

6 Chuyển đĩa số 1 từ cọc b sang cọc a, kí hiệu là (101)2.

Chữ số 1 thứ ba (khác số 0 trong bước 5) biểu thị đĩa số 3 đã được chuyển.Ba chữ số

1, 0, 1 khác nhau biểu thị ba đĩa ở ba cọc khác nhau

7 Chuyển đĩa số 2 từ cọc b sang cọc c, kí hiệu là (110)2

Chữ số 1 thứ n (khác số 0 trong bước 5) biểu thị đĩa số 3 đã được chuyển.Ba chữ số 1,

0, 1 biểu thị ba đĩa ở ba cột khác nhau

8 Chuyển đĩa 2 từ cột b sang c

9 Chuyển đĩa 1 từ b sang c

Với cách thực hiện trên thì nguồn và cái cọc đích cho lần di chuyển thứ k có thể được tìmthấy từ sự biểu diễn nhị phân của k sử dụng toán tử phân theo bit (bitwise operations) Để sửdụng cú pháp của ngôn ngữ lập trình C, di chuyển k từ cọc (k& k-1)% 3 tới cọc ((k|k-1)+1)%3,nơi mà cái đĩa bắt đầu rừ cọc 0 và kết thúc ở cọc 1 và 2 tùy theo số đĩa là chãn hay lẻ.Thêm một phát biểu khác là từ (k-(k-& k )% 3) tới cọc (k+(k-& k )% 3)

2.2.2 Thuật toán

Ta có mã giả:

1 # Ham thuc hien viec di chuyen

2 def move(size_t ndisk, int source, int dest, int mid):

Trang 24

33 if(ndisk > MAX_DISK || ndisk == 0) {

34 fprintf(stderr, "Số đĩa không hợp lệ \n ");

47 * Description: Di chuyển ndisk đĩa từ cột src đến cột dst,

48 * có thể sử dụng cột trung gian mid

Trang 25

Xét với ndisk = 5 ta có:

25

Ngày đăng: 28/02/2016, 00:20

HÌNH ẢNH LIÊN QUAN

Hình 1: Quân Hậu ở ô (3,2) chiếm cột thứ 2, đường chéo loại I thứ 5 và đường chéo loại II thứ 1 - Bài tập môn học kỹ thuật lập trình
Hình 1 Quân Hậu ở ô (3,2) chiếm cột thứ 2, đường chéo loại I thứ 5 và đường chéo loại II thứ 1 (Trang 3)

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w