Dijkstra E. Với mỗi giá trị N cho trước hãy sinh N số đầu tiên theo trật tự tăng dần là tích các lũy thừa của 2, 3 và 5.
Dữ liệu vào: tệp văn bản LUYTHUA.INP Chứa số tự nhiên N, 1 N 1000. Dữ liệu ra: tệp văn bản LUYTHUA.OUT N số tìm được, mỗi dịng một số.
Thuật toán
Gọi S là tập các số cần tìm. Ta có (i) 1 S
(ii) Nếu x S thì 2x, 3x, 5x S.
Giả sử các phần tử trong S được sắp tăng và ta đã tìm được phần tử thứ i. Ta kí hiệu S(i) = { a1, a2,…,ai }. Để tìm phần tử thứ i+1 ta nhận xét
ai+1 = Min { 2x, 3y, 5z | x, y, z S(i), 2x > ai, 3y > ai, 5z > ai }
Ta sử dụng 3 biến i2, i3, i5 để ghi nhận các chỉ số trong S sao cho ai2 = x, ai3 = y và ai5 = z. Các biến a[1], i2, i3 và i5 được khởi trị 1.
Khi đó hàm Next(i) sinh phần tử sát sau phần tử A[i] sẽ như sau:
function Next(i: integer): integer; begin
while (a[i2] * 2 <= a[i]) do i2 := i2 + 1; while (a[i3] * 3 <= a[i]) do i3 := i3 + 1; while (a[i5] * 5 <= a[i]) do i5 := i5 + 1; Next := Min(a[i2]*2, a[i3]*3, a[i5]*5); end;
(* Pascal *)
(*************************************** LUY THUA cua 2, 3, 5
*************************************) program LuyThua;
uses crt;
const bl = #32; mn = 1001; fn = 'LUYTHUA.INP'; gn = 'LUYTHUA.OUT'; type ml1 = array[0..mn] of longint;
var f,g: text; n: integer; a: ml1;
procedure Doc: tự viết;
function Min(a,b,c: longint): tự viết; function Next(i: integer): tự viết; procedure Sinh; var i: longint; begin assign(g,gn); rewrite(g); LUYTHUA.INP LUYTHUA.OUT 12 1 2 3 4 5 6 8 9 10 12 15 16
a[1] := 1; writeln(g,1); i2 := 1; i3 := 1; i5 := 1; for i := 2 to n do begin a[i] := Next(i-1); writeln(g,a[i]); end; close(g); end; BEGIN Doc; Sinh; END. // C# using System; using System.IO; namespace SangTao2 { /*-------------------------------------------* Luy thua 2, 3, 5 * ------------------------------------------*/ class LuyThua235 {
const string fn = "LuyThua.inp"; const string gn = "LuyThua.out";
static public int n; // so luong phan tu static public int[] a;
static void Main(){
Doc(); Sinh(); Ghi(); XemKetQua(); Console.WriteLine("\n fini");
Console.ReadLine(); } // Main
static public void Doc()
{ n = int.Parse(File.ReadAllText(fn).Trim()); } static public void Sinh(){
a = new int[n];
int i2 = 0, i3 = 0, i5 = 0; a[0] = 1; int n1 = n-1;
for (int i = 0; i < n1; ++i){ // Next while (a[i2] * 2 <= a[i]) ++i2; while (a[i3] * 3 <= a[i]) ++i3; while (a[i5] * 5 <= a[i]) ++i5;
a[i + 1] = Min(a[i2] * 2, a[i3] * 3, a[i5] * 5); }
}
static public int Min(int x, int y, int z) : tự viết static public void Ghi(){
StreamWriter g = new StreamWriter(gn);
for (int i = 0; i < n; ++i) g.WriteLine(a[i]); g.Close();
}
static void XemKetQua(): tự viết } // LuyThua235
Chương 3 Trò chơi
Các bài tốn trị chơi khá đa dạng và thường là khó. Chúng ta xét loại trò chơi thứ nhất với các gỉa thiết sau đây:
1. Trò chơi gồm hai đấu thủ là A và B, luân phiên nhau, mỗi người đi một nước. Ta luôn giả thiết đấu thủ đi trước là A.
2. Hai đấu thủ đều chơi rất giỏi, nghĩa là có khả năng tính trước mọi nước đi.
3. Đấu thủ nào đến lượt mình khơng thể đi được nữa thì chịu thua và ván chơi kết thúc. 4. Khơng có thế hịa, sau hữu hạn nước đi sẽ xác định được ai thắng, ai thua.
Giả thiết chơi giỏi nhằm tránh các trường hợp “ăn may”, tức là các trường hợp do đối phương hớ hênh mà đi lạc nước. Điều này tương đương với giả thiết cả hai đấu thủ đều có thể tính trước mọi nước đi (với loại trò chơi hữu hạn) hoặc cả hai đấu thủ đều biết cách đi tốt nhất. Để tiện trình bày chúng ta gọi các trị chơi loại này là chơi cờ, mỗi thế của bàn cờ là một tình huống với dữ liệu cụ thể, ta thường gọi là một
cấu hình.
Các bài tốn tin liên quan đến loại trò chơi này thường là:
Lập trình để xác định với một thế cờ cho trước thì người đi trước (đấu thủ A) sẽ thắng hay thua.
Lập trình để máy tính chơi với người. Dĩ nhiên chương trình bạn lập ra là dành cho máy tính.
Lập trình để hai máy tính chơi với nhau.
Với loại trị chơi này có một heuristic mang tính chỉ đạo sau đây:
Trước hết cần xác định được một tính chất T thỏa các điều kiện sau đây: a) Thế thua cuối cùng thỏa T,
b) Mọi nước đi luôn luôn biến T thành V = not T, c) Tồn tại một nước đi để biến V thành T.
Tính chất T được gọi là bất biến thua của trò chơi.
Việc chuyển thế X thành not X thường được gọi là lật thế X. Các qui tắc a - c có thể phát biểu lại như sau:
T được gọi là bất biến thua nếu
a') Thế thua cuối cùng thỏa T,
b') Mọi nước đi từ T đều lật T thành V, c') Tồn tại một nước đi để lật V thành T.
Đấu thủ nào có cách đẩy đấu thủ khác vào thế thua T thì đấu thủ đó sẽ thắng.
Đấu thủ nào không thể đẩy đấu thủ khác vào thế thua T thì đấu thủ đó sẽ thua.
Nước đi ở đây được hiểu là nước đi hợp lệ tức là nước đi tuân thủ các qui định của trị chơi, thí dụ "xe liền, pháo cách" trong cờ tướng qui định rằng quân xe có thể "ăn" trực tiếp các quân của đối phương nằm trên đường đi của nó, cịn qn pháo thì phải "ăn" qua một qn đệm.
Điểm khó nhất của loại tốn này là xác định bất biến thua.