Tải bản đầy đủ (.doc) (35 trang)

SKKN kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán tin học

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (224.25 KB, 35 trang )

Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học
MỤC LỤC
A. ĐẶT VẤN ĐỀ......................................................................................................3
I. LÝ DO CHỌN ĐỀ TÀI...................................................................................... 3
II. CƠ SỞ LÝ LUẬN............................................................................................. 4
III. CƠ SỞ THỰC TIỄN........................................................................................ 4
1. Thuận lợi:........................................................................................................4
2. Khó khăn:........................................................................................................4
IV. NỘI DUNG.......................................................................................................5
V. PHẠM VI ỨNG DỤNG CỦA ĐỀ TÀI............................................................. 5
B. NỘI DUNG.......................................................................................................... 6
I. CÁC KIẾN THỨC CƠ BẢN VỀ BIT TRẠNG THÁI....................................... 6
1. Phép đảo bit Not..............................................................................................6
2. Phép AND....................................................................................................... 7
3. Phép OR.......................................................................................................... 7
4. Phép phủ định NOT........................................................................................ 8
5. Phép XOR....................................................................................................... 8
6. Phép dịch trái <<.............................................................................................9
7. Phép dịch phải >>........................................................................................... 9
II. CÁC BÀI TOÁN MINH HỌA VỀ XỬ LÝ BIT TRẠNG THÁI....................10
Bài toán 1.......................................................................................................... 10
Bài toán 2.......................................................................................................... 10
Bài toán 3:.........................................................................................................12
Bài toán 4: Phép cộng....................................................................................... 12
Bài toán 5. Tập con........................................................................................... 13
Bài tốn 6: Trị chơi NIM..................................................................................13
Bài tốn 7. Số khác............................................................................................16
Bài tốn 8. Xâu cơ lập.......................................................................................18
Bài tốn 9. FIRSTROW...................................................................................20


Bài tốn 10. Dãy số..........................................................................................21
Bài toán 11: Đầu bếp.........................................................................................23
Trang 1


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

Bài toán 12: Biểu diễn trạng thái.......................................................................25
Bài tốn 13: Quy hoạch động............................................................................26
Bài tốn 14: Chọn ơ...........................................................................................28
Bài tốn 15: Chuyến du lịch - TRIP..................................................................32
Bài tốn 16: Cơ gái chăn bò - COWGIRL........................................................ 33
C. KẾT LUẬN........................................................................................................34

Trang 2


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

A. ĐẶT VẤN ĐỀ
I. LÝ DO CHỌN ĐỀ TÀI
Trong thời đại hiện nay công nghệ thông tin đã thực sự bùng nổ và đã có tác
động rất lớn đến với công cuộc phát triển kinh tế - xã hội của con người, của đất
nước (và thực tế ta có thể nói rằng ta đang sống trong kỉ nguyên số, kỉ nguyên
công nghệ thông tin). Đảng và nhà nước ta đã xác định rõ là để đất nước phát triển
thì một trong những yếu tố làm nền tảng là làm sao các ứng dụng của Tin học –
công nghệ thông tin phải đưa vào triệt để trong các lĩnh vực của xã hội.
Những yêu cầu đẩy mạnh của các ứng dụng công nghệ thông tin, đào tạo
nguồn nhân lực đáp ứng u cầu Cơng nghiệp hóa, Hiện đại hóa, mở cửa và hội
nhập, hướng đến nền kinh tế tri thức của đất nước ta nói riêng, thế giới nói chung.

Chính vì xác định được tầm quan trọng đó, tơi nghĩ đào tạo và cung cấp cho
đất nước những nhà lập trình viên giỏi là việc làm hết sức cần thiết.
Tin học lập trình là một mơn học khó đối với học sinh THPT. Làm thế nào
để học sinh hiểu, học tốt, yêu thích và tham gia tốt các kỳ thi học sinh giỏi Tin học
là điều mà nhiều giáo viên dạy tin học trăn trở.
Là giáo viên dạy tin nhiều năm tôi thấy việc trang bị cho các em học sinh
các kiến thức về thuật toán và giải quyết vấn đề một cách khoa học là rất cần thiết,
điều đó giúp học sinh hứng thú hơn trong học tập bởi các em tự kiến tạo tri thức
hoặc tham gia vào việc kiến tạo tri thức cho mình dựa trên tri thức đã có, bổ sung
và làm cho tri thức cũ hoàn chỉnh hơn. Học sinh học tập tự giác, tích cực, vừa kiến
tạo tri thức, vừa học đựơc cách giải quyết vấn đề, lại vừa rèn luyện được những
đức tính quý báu như tính chủ động, tích cực, tính kiên trì vượt khó, tính kế hoạch
và thói quen tự kiểm tra…
Trong đề tài này, tôi xin được đề cập tới “Kỹ thuật dùng bit trạng thái để
xử lý hiệu quả bài toán tin học”.
Như chúng ta đã biết, bộ nhớ của máy tính được lưu trữ dưới dạng mã hóa
của hệ nhị phân theo bảng mã IISCI. Bản chất máy tính chỉ hiểu được mã nhị phân
dưới dạng dãy số 0 và 1. Mỗi con số như vậy được gọi là một bit. Bít là đơn vị nhỏ
nhất để lưu trữ dữ liệu trong máy tính điện tử, mỗi bit chứa số 0 hoặc 1. Chính vì
vậy khi lập trình nếu ta chuyển bài tốn về xử lý dưới dạng bit thì tốc độ của
chương trình được đẩy nhanh rất nhiều.

Trang 3


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài tốn Tin học

Ngơn ngữ lập trình cung cấp cho chúng ta những tốn tử để chúng ta có thể
thao tác trên bit như các phép cơ bản and, or, not, xor, dịch trái, dịch phải. Trong đề
tài này tôi sẽ cung cấp kiến thức về việc sử dụng các phép tốn logic từ đó giúp

cho việc thiết kế các biểu thức logic dùng rất nhiêu trong các phép tốn điều kiện
được nhanh chóng, chính xác, hiệu quả. Hơn nữa trong đề tài này sẽ phân tích sự
hiệu quả của việc chuyển bài toán từ phương pháp xử lý thông thường sang
phương pháp xử lý bit.

II. CƠ SỞ LÝ LUẬN
Qua nhiều năm giảng dạy, bồi dưỡng học sinh giỏi môn Tin học tôi nhận
thấy:
- Việc trang bị cho các em học sinh các kiến thức về thuật toán và giải quyết
vấn đề một cách khoa học là rất cần thiết.
- Phương pháp dùng kỹ thuật bit trạng thái để xử lý thực sự hiệu quả bài toán
tin học.
- Trong tất cả các kỳ thi chọn học sinh giỏi Tỉnh, các bài toán dùng phương
pháp bit trạng thái thực sự rất hiệu quả

III. CƠ SỞ THỰC TIỄN
Một số thuận lợi và khó khăn khi thực hiện chuyên đề sáng kiến kinh
nghiệm ở trường.
1. Thuận lợi:
- Môn Tin học là một môn học mới nhưng được sự ủng hộ của Cấp trên, Sở
giáo dục và Đào tạo, cấp Ủy, nhà trường đã tạo được điều kiện sắm sửa phòng máy
và các trang thiết bị cần thiết.
- Trường có cơ sở vật chất tốt.
- Học sinh rất hứng thú đối với môn học này
- Mơn học có ứng dụng rất lớn trong thực tiễn nên học sinh rất chịu khó
nghiên cứu học tập.
- Hàng năm kết quả thi học sinh giỏi môn Tin đạt kết quả cao nên tạo động
lực lớn cho học sinh quyết tâm học tập.
2. Khó khăn:
Trang 4



Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

- Các tài liệu bồi dưỡng học sinh giỏi mơn tin cịn rất ít.
- Giáo viên đủ chuẩn để bồi dưỡng học sinh giỏi môn tin cịn hạn chế.
- Là mơn học khó, địi hỏi người học hội tụ quá nhiều phẩm chất như: Thông
minh, tư duy tốt, kiên trì, có năng khiếu về lập trình, cẩn thận, nhanh nhạnh nhưng
chính xác tuyệt đối trong từng kỹ thuật nhỏ...

IV. NỘI DUNG
- Các kiến thức cơ bản về bit trạng thái
- Các bài toán minh họa ứng dụng xử lý bit

V. PHẠM VI ỨNG DỤNG CỦA ĐỀ TÀI
Nội dung của đề tài được sử dụng làm tài liệu cho các học sinh ở các trường
THCS, THPT yêu thích mơn tin học lập trình. Đề tài cũng là tài liệu cho các đồng
nghiệp trong và ngoài Tỉnh yêu thích bộ mơn Tin học.

Trang 5


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

B. NỘI DUNG
I. CÁC KIẾN THỨC CƠ BẢN VỀ BIT TRẠNG THÁI
Quy ươc về vị trí của các bit: Mỗi byte bao gồm 8 bit được mã số từ phải
sang trái còn gọi là bit thấp đến bit cao. Bit nằm ở bên phải được xem là thấp hơn
bit nằm ở bên trái. Các bit được đánh số như sau: 7 6 5 4 3 2 1 0.
Mỗi bit có thể nhận 1 trong 2 giá trị là 0 hoặc 1. Tại mỗi thời điểm thực hiện

chương trình mỗi bit đươc nhận giá trị xác định. Mọi số nguyên trong máy đều
biểu diễn dưới dạng nhị phân, ví dụ số 19 được biểu diễn như sau: Bit 7 6 5 4 3 2 1
0.
Giá trị 0 0 0 1 0 0 1 1 (số 19).
Các phép tốn logic trên bit có các phép cơ bản and, or, not, xor, dịch trái,
dịch phải.
Các phép toán sau đây thực hiện trên các giá trị nguyên và cho kết quả là các
giá trị nguyên.
1. Phép đảo bit Not
Đổi giá trị của mọi bit từ 0 thành 1 và ngược lại.
Trong C phép đảo được kí hiệu là ~ .
Ví dụ
Các toán thử thao tác trên bit (bitwise)
Dữ liệu lưu trữ trong máy tính dưới dạng nhị phân 0 1,
ví dụ:
unsigned char a = 12;
Thì lưu trữ dưới bộ nhớ là: 0000 1100
Tương tự
unsigned int b = 95;
Lưu trữ b dưới bộ nhớ là: 0000 0000 0000 0000 0000 0000 0101 1111
Không gian lưu trữ kiểu unsigned int lớn hơn unsigned char do unsigned
int dùng 32 bits để biểu diễn cịn unsigned char dùng 8 bits để biểu diễn.
Các tốn tử thao tác trên bit

Phép thao tác trên bit

Kí hiệu
Trang 6



Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

Phép AND

&

Phép OR

|

Phép phủ định NOT

~

Phép XOR

^

Phép dịch trái - Shift left

<<

Phép dịch phải - Shift right

>>

2. Phép AND
Kí hiệu: &
Bảng chân trị


A

B

A&B

0

0

0

0

1

0

1

0

0

1

1

1


Phép AND chỉ có giá trị 1 nếu cả hai tốn hạng đều có giá trị 1.
Ví dụ:
A

0000 1100

B

0101 0101

C = A & B 0000 0100
3. Phép OR
Kí hiệu: |
Bảng chân trị

A

B

A|B

0

0

0

0

1


1

1

0

1
Trang 7


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài tốn Tin học

1

1

1

Phép OR chỉ có giá trị 0 nếu cả hai tốn hạng đều có giá trị 0.
Ví dụ:
A

0000 1100

B

0101 0101

C=A|B


0101 1101

4. Phép phủ định NOT
Kí hiệu: ~
Bảng chân trị

A

~A

0

1

1

0

Phép NOT đảo bit 1 thành 0 và ngược lại.
Ví dụ
A

0000 1100

B = ~A 1111 0011
5. Phép XOR
Kí hiệu: ^
Bảng chân trị


A

B

A^ B

0

0

0

0

1

1

1

0

1

1

1

0


Phép XOR chỉ có giá trị 0 nếu cả hai tốn hạng có cùng giá trị, cùng là giá
trị 1, hay cùng là giá trị 0.
Ví dụ
Trang 8


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

A

0000 1100

B

0101 0101

C=A^B

0101 1001

6. Phép dịch trái <<
Kí hiệu: <<
Phép dịch trái n bit tương đương với phép nhân cho 2n.
Ví dụ
A

0000 1100

<—
B = A << 2


0011 0000

7. Phép dịch phải >>
Kí hiệu: >>
Phép dịch phải n bit tương đương với phép chia cho 2n.
Ví dụ
A

0000 1100
—>

B = A >> 2

0000 0011

Các ví dụ thao tác cơ bản trên bit
AND
unsigned char a = 5;

// 00000101(5)

unsigned char b = 6;

// 00000110(6)

unsigned char c = a & b; // 00000100(4)
OR
unsigned char a = 5;


// 00000101(5)

unsigned char b = 6;

// 00000110(6)

unsigned char c = a | b;

// 00000111(7)

NOT
Trang 9


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

unsigned char a = 5;

// 00000101(5)

unsigned char b = ~a;

// 11111010(250)

unsigned char a = 5;

// 00000101(5)

unsigned char b = 6;


// 00000110(6)

int c = a ^ b;

// 00000011(3)

unsigned char a = 5;

// 00000101(5)

XOR

Dịch trái <<

unsigned char b = a << 4; // 01010000(80)
Dịch phải >>
unsigned char a = 5;

// 00000101(5)

unsigned char b = a >> 1; // 00000010(2)

II. CÁC BÀI TOÁN MINH HỌA VỀ XỬ LÝ BIT TRẠNG THÁI
Bài toán 1: Lấy bit
Ứng dụng 1. Viết hàm getbit(x,i) cho giá trị của bit thứ i(tính từ phải sang)
của số nguyên x.
char Getbit(char x, i)
{
return (x Shr i) && 1;
}

Bài toán 2: Lấy số
Ứng dụng 2
Viết hàm GetNum(x, j, i) cho số tạo bởi các bit tính từ trái qua phải là bit thứ
j đến bit thứ i (j>=i).
Ví dụ
GetNum(103,5,1)=19 vì 103=0000 0000 0110 0111
do đó GetNum(103,5,1)= 0000 0000 0001 0011 =19
Bài giải:
Trang 10


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

Gọi số lượng bit của x là L(x), trước hết ta phải hóa 0 các bit nằm ở bên trái
bit thứ j. Muốn vậy ta dịch x qua trái k= L(x)-1-j bit. Sau đó ta chuyển giá trị thu
được về cấu hình ban đầu tức là dịch phải k bit rồi tiếp tục dịch phải i bit để thu
được số cần tìm. Hàm sizeof(x) cho ta số byte trong x. Nhân giá trị này với 8 ta sẽ
được số bit trong x. Vì phép nhân 8 tương đuơng với phép dịch trái 3 bit nên
sizeof(x) Shl 3 cho ta số bit trong x. Vì các bit được mã số từ phải sang trái bắt đầu
từ bit 0 cho nên số hiệu cao nhất trong x sẽ là sizeof(x) Shl 3 -1
char Getnum(char x, j, i)
{
char k;
k= (sizeof(x) Shl 3)-1-j
return x Shl k Shr k Shr i;
}
Đối với 2 hàm Getbit và GetNum ở trên ta sé có được một số ứng dụng như
chuyển từ số tự nhiên sang biểu diễn nhị phân, thập lục phân, bát phân.
Ngồi ra dựa vào các phép tốn logic cơ bản này ta có thể có một số ứng
dụng khác như:

- Viết hàm biểu diễn dưới dạng nhị phân mà không có số 0 thừa ở đầu
- Viết hàm đảo trật tự các bit trong 1 số ví dụ: 1011 (11) tahnhf 1101(13)
Thủ tục Batbit(x,i) gán trị 1 cho bit thứ i trong byte x
Mô tả: Ta chuyển 1 đến vị trí i tương ứng sau đó thực hiện phép cộng logic
với byte x
Ví dụ này chỉ mang tính minh họa
char Batbit(char x, i)
{
return x OR(1 Shl i);
}
Thu tục Tatbit(x, i) gán trị 0 cho bit thứ i trong byte
Mô tả: Ta chuyển 1 đến vị trí i tương ứng, sau đó đảo byte này để được byte
có 0 ở vị trí i và 1 ở các bit cịn lại, cuối cùng thực hiện phép nhân logic với byte x.
Trang 11


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài tốn Tin học

Ví dụ này chỉ mang tính minh họa
Char Tatbit(char x, i)
{
return NOT(1 Shl i) And x;
}
Thao tác logic: giá trị bằng 0 được coi như là false (sai); giá trị khác 0 được
coi như là true (đúng). Các toán tử là '!' (NOT), '&&' (AND), và '||' (OR) (khơng có
tốn tử XOR logic; nhưng có thể viết bằng NOT, AND, OR)
Thao tác bit: bit 0 được coi như là false (sai); bit 1 được coi như là true
(đúng). Các toán tử là '~' (NOT), '&' (AND), '|' (OR) va` '^' (XOR)
Về phép dịch bit: các toán tử là '>>' (dịch qua phải) và '<<' (dịch qua trái)
(tương ứng với phép dịch cho số không dấu trong hợp ngữ; C khơng có tốn tử

dịch qua phải có dấu)
Về bật / tắt / đảo bit:
X AND 0 -> 0; X AND 1 -> X: dùng để tắt bit
X OR 0 -> X; X OR 1 -> 1: dùng để bật bit
X XOR 0 -> X; X XOR 1 -> ~X: dùng để đảo bit
Bài toán 3:
Phép nhân Ai Cập cổ đại chỉ ra cách để có thể nhân hai số ngun tùy thích a
và b (trong đó a lớn hơn b) mà chỉ cần sử dụng thao tác dịch chuyển bit và phép
cộng:
c=0
while b ≠ 0
if (b and 1) ≠ 0
c=c+a
left shift a by 1
right shift b by 1
return c
Bài toán 4: Phép cộng
Trang 12


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài tốn Tin học

Một ví dụ nữa là mã giải của phép cộng, chỉ ra cách để tính tổng của hai số
nguyên a và b sử dụng các toán tử thao tác bit và phép thử số 0:
while a ≠ 0
c = b and a
b = b xor a
left shift c by 1
a=c
return b

Lưu ý: các dấu = trong các ví dụ trên là phép gán chứ khơng phải là phép
tương đương.
Bài toán 5. Tập con
Duyệt qua tất cả các tập con có k phần tử

int s = (1 << k) - 1;
while (!(s & 1 << N))
{
//làm gì đó với s
int lo = s & ~(s - 1);

//bit 1 thấp nhất

int lz = (s + lo) & ~s;

//bit 0 thấp nhất trên lo

s |= lz;

//thêm lz vào tập hợp

s &= ~(lz - 1);

//reset bit phía dưới lz

s |= (lz / lo / 2) - 1;

//đặt lại đúng số bit ở cuối

}

Trong C, dịng cuối có thể viết là s |= (lz >> ffs(lo)) - 1 để tránh phép chia.
Xác định x ? y : -y, trong đó x bằng 0 hoặc 1 (-x ^ y) + x

Bài toán 6: Trị chơi NIM
Có n đống sỏi, mỗi đống có một số viên sỏi. Hai người chơi luân phiên nhau
đi như sau. Đến lượt người nào, người đó tùy chọn một đống sỏi để bốc số viên sỏi
Trang 13


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

trong đống đã chọn và buộc phải bốc, ít nhất là 1 viên, nhiều nhất là cả đống. Ai
bốc được những viên sỏi cuối cùng sẽ thắng. Lập chương trình tổ chức chơi giữa
máy tính và người theo các yêu cầu sau:
- Số lượng sỏi lúc đầu được quy định trước
- Số lượng sỏi trong mỗi đống được sinh ngẫu nhiên
- Máy sẽ gieo xu để xác định máy hoặc người đi trước.
- Có thơng báo kết quả mỗi ván.
Bài giải:
Có một lớp trị chơi 2 người thỏa mãn nguyên tắc sau: Tồn tại một thế có
tính chất T sao cho với mọi cách đi đều dẫn đến một thế khơng có tính chất T mà ta
kí hiệu là NT = NOT (T) và từ NT ln ln tìm được một cách đi để dẫn đến một
thế có tính chất T.
Nếu thế thắng chung cuộc có tính chất T thì ai gặp phải thế đó sẽ thua, do đó
ta phải tìm mọi cách "nhường" thế có tính chất T cho đối phương.
Tính chất T nói trên được gọi là bất biến của trò chơi. Khi chung cuộc mọi
đống sỏi đều hết cho nên S=0.
Giả sử ta có n đống sỏi. Gọi a i là số sỏi trong đống thứ i; i=1..n. Ta lấy tổng
loại trừ của các ai
S= a1 XOR a2 XOR .... XOR an.

Ta sẽ chứng minh rằng bất biến (tính chất T) của trị chơi NIM chính là S=0.
Ta sẽ sử dụng một số tính chất sau đây của phép XOR:
Tính chất 1: a XOR b =0 khi và chỉ khi a=b (suy từ định nghĩa).
Tính chất 2: a XOR b = b XOR a (tính giao hốn)
Tính chất 3: (a XOR b) XOR c = a XOR (b XOR c) (Tính kết hợp)
Tính chất 4: a XOR 0= a
Ta thừa nhận định lý sau:
Định lý: Nếu S<>0 thì tồn tại một ai thỏa mãn ai XOR S < ai
Ta minh họa định lý trên qua ví dụ sau. Giả sử n= 3 và a 1= 9= B1001
a2= 8= B1000
a3= 3= B0011
S= 2= B0010
Trang 14


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài tốn Tin học

Ta có a1 XOR S= 11>9 =a1
a2 XOR S= 10>8 = a2
a3 XOR S= 1< a3
Vậy a3 chính là số tìm được theo định lý trên
Nhận xét: Nếu S=0 và có ít nhất một đống sỏi với số sỏi khác 0 thì với mọi
cách đi ta ln ln có tổng loai trừ của các đống sỏi là một số khác 0.
Thật vậy, khơng làm mất tính tổng quát ta giải sử rằng đống sỏi được chọn
để bốc là a1. Giả sử rằng sau khi bốc đống a1 ta cịn lại b1 viên sỏi. Có thế coi việc
bốc sỏi là thay đống a1 bằng b1, vì a1 XOR a1= 0 nên tổng loài trừ thu được sau khi
bốc sẽ là:
P= b1 XOR (a2 XOR...XOR an)
Nếu P = 0 thì b1=(a2 Xor ... Xor an)=0 XOR (a2 Xor...Xor an)= (a1 Xor a1) Xor
(a2 Xor....Xor an)= = a1 Xor (a1 Xor a2 Xor...Xor an)= a1 Xor S.

Vì S =0 nên b1 = a1 XOR 0= a1.
Điều này chứng tỏ không viên sỏi nào được bốc tại đống a 1, trái với luật
chơi. Vậy sau khi bốc ta phải có P<>0
Hệ quả: Gọi S và P lần lượt là tổng loại trừ của các đống sỏi trước và sau
một bước đi. Ta có, nếu S<>0 thì có một cách đi để P= 0;
Thật vậy, theo định lý trên ta tìm được một số ai để (ai XOR S)< ai.
Đặt bi = ai- (ai XOR S). Ta bốc bi viên sỏi từ đống ai, đống này sẽ còn (ai
XOR S) viên.
Khi đó P = (ai XOR S) XOR (ai XOR S)=0 (đpcm).
Từ nhận xét và hệ quả nói trên ta suy ra S=0 chính là bất biến của trị chơi
NIM.
Nước đi của đối thủ thơng minh khi đó sẽ bao gồm hai bước sau:
Bước 1: Tính S= a1 XOR a2 XOR a3....XOR an
Bước 2: Nếu S <> 0: thực hiện các bước sau:
Bước 2a. Duyệt các đống sỏi để tìm đống ai thỏa điều kiện (ai XOR S)< ai
Bước 2b. Bốc ai - (ai XOR S) viên từ đống ai tìm được . Số sỏi còn lại của
đống này sẽ là (ai XOR S).

Trang 15


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

Nếu S=0: chọn đống lớn để bốc 1 viên cốt làm cho đối phương khó phát
hiện nguy cơ thua của ta.
Như vậy thủ tục NIM - Trò chơi NIM với m đống sỏi gồm các bước sau
Bước 1: Kiểm tra giá trị hợp lệ của m
Bước 2: Khởi trị;
Bước 2a. Sinh ngẫu nhiên các giá trị ai>=1, i=1..n.
Bước 2b. Gieo xu để xác định ai đi trước, người hay máy

Bước 3. Chơi theo sơ đồ sau
Repeat
If Ban then Bandi
Else Maydi;
Ban:= NOT Ban;
Xem;
Until Thua;
Bước 4: Thông báo kết quả thắng- thua giữa người và máy.
Trong đó thủ tục Maydi được triển khai theo sơ đồ đối thủ thơng minh trình
bày ở trên.
Trị chơi này nhìn chung đơn giản, dễ dàng cài đặt, nếu có thêm chút đồ họa
vào là quá ngon lành. Chúc các bạn thấy vui vẻ với trị chơi này.
Bài tốn 7. Số khác
Xét một dãy gồm N số nguyên A1, A2, A3,…,AN-1, AN. Dãy có tính chất sau:
Tất cả các số đều xuất hiện một số chẵn lần
Có duy nhất một số x xuất hiện đúng một lần gọi là số khác.
Yêu cầu: Hãy tìm số khác của một dãy cho trước.
Dữ liệu vào: Trong file văn bản SK.INP gồm:
Dòng đầu là số N (N≤107).
N dòng tiếp theo, dòng thứ i là số Ai có giá trị tuyệt đối khơng q 109
Ví dụ:
SK.INP SK.OUT
Trang 16


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

5

1


2
1
2
3
3
Cách giải:
Ta chỉ việc dùng phép toán triệt tiêu ^ để loại trừ 2 số giống nhau, khi đó kết
quả sẽ là số chỉ xuất hiện 1 lần duy nhất.
int kq=0,n,a;
int main()
{
freopen("sk.inp","r",stdin);
freopen("sk.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a;
kq=kq^a;
}
cout<}
Phép toán xor là phép toán triệt tiêu bit: 2 bit giống nhau cho giá trị 0; 2 bít
khác nhau cho giá trị 1.
Ta xét dãy n=5 số: 1, 3, 7, 3, 1
Trang 17



Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

Lần 1: m=0 (tức là 00 trong hệ nhị phân); x= 1( = 01 trong hệ nhị phân)
M= 0 xor 1 ( tức là 00 xor 01= 01) = 1;
Lần 2: m=1 (tức là 01 trong hệ nhị phân); x= 3( = 11 trong hệ nhị phân)
M= 3 xor 1 ( tức là 11 xor 01= 10) = 2;
Lần 3: m=2 (tức là 01 trong hệ nhị phân); x= 7( = 111 trong hệ nhị phân)
M= 7 xor 2 ( tức là 111 xor010= 101) = 5;
Lần 4: m=5 (tức là 101 trong hệ nhị phân); x= 3( = 011 trong hệ nhị phân)
M= 5 xor 3 ( tức là 101 xor 011= 110) = 6;
Lần 5: m=6 (tức là 110 trong hệ nhị phân); x= 1( = 001 trong hệ nhị phân)
M= 6 xor 1 ( tức là 110 xor 001= 111) = 7;
Chú ý: dãy nhị phân
1= 0000
1=0001
2=0010
3= 0011
4=0100
5= 0101
6= 0110
7=0111
Bài toán 8. Xâu cơ lập
Cho trước N (N≤10000) xâu kí tự độ dài khơng q 255 kí tự. Xâu cơ lập
được định nghĩa là xâu chỉ xuất hiện duy nhất 1 lần trong N xâu đã cho. Các xâu
cịn lại ln xuất hiện một số chẵn lần.
u cầu: Tìm xâu cơ lập từ N xâu đã cho. Với dữ liệu vào là hoàn toàn đúng
đắn.
Dữ liệu vào: Trong file văn bản SINGLE.INP gồm N dòng biểu diễn N xâu
đã cho ban đầu.

Kết quả ra: Ra file văn bản SINGLE.OUT xâu cơ lập
Ví dụ:

Trang 18


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

SINGLE.INP

SINGLE.OUT

SINGLE.INP

SINGLE.OUT

-How are you?

-Hello

-Hello, sir.

-Go away!

-I am fine, thank you.
And you?

-Go away!

-I am ok.


-Can
you?

-How are you?

-Hello, sir.

-Hello.

-Can
you?

-I am fine, thank you.
And you?

I

I

help

help

-I am ok.
Cách giải:
Cách làm như bài số khác
- Dùng mảng a[300] khởi tạo bằng 0
- Đọc từng xâu vào biến xâu s
+ cho (i=1 đến độ dài xâu s

a[i] = a[i]^s[i]
Xuất kết quả:
for(i=1; i<256; i++ ) cout<< char(a[i])}
string s;
long long i,n,a[300];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
freopen("SINGLE.inp","r",stdin);
freopen("SINGLE.out","w",stdout);
for(i=0;i<255;i++)a[i]=0;
while (getline(cin,s))
{
Trang 19


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

for(i=0;i}
for(i=0;i<255;i++)cout<}
Bài toán 9. FIRSTROW
Cho một văn bản gồm n dịng (1≤n≤50000) mỗi dịng gồm khơng quá 255
ký tự. Trong file văn bản đã cho có một dòng đặc biệt chỉ xuất hiện trong văn bản
đúng một lần, còn các dòng khác đều bị lặp lại và hơn thế nữa lại là một số lần
chẵn. Tìm dịng thơng tin đặc biệt này
Dữ liệu vào: Trong file văn bản ‘FIRSTROW.INP’ chứa các dòng của văn

bản đã cho, trong đó dịng cuối cùng của file có dấu # đánh dấu kết thúc của văn
bản và dịng này khơng được tính vào văn bản
Kết quả ra: Ra file văn bản ‘FIRSTROW.OUT’ dịng đặc biệt tìm được
Ví dụ:

FIRSTROW.INP

FIRSTROW.OUT

I Love You

Phan Van The

Phan Van The
I Love You
I Hate You
Love forever
I Hate You
Love forever
#

Cách giải:
Hồn tồn giống bài tốn 5.
string s;
long long i,n,a[300];
int main()
Trang 20


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học


{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
freopen("FIRSTROW.inp","r",stdin);
freopen("FIRSTROW.out","w",stdout);
for(i=0;i<255;i++)a[i]=0;
while (getline(cin,s))
{
for(i=0;i}
for(i=0;i<255;i++)cout<}
Bài toán 10. Dãy số
Với số ngun khơng âm X bất kì, bạn có thể tạo dãy số A[X] như sau:
 Số đầu tiên của dãy là X.
 Sau một số lẻ y sẽ là số y – 1.
 Sau một số chẵn y sẽ là số y / 2.
Ví dụ với X = 60. Ta có dãy số 60, 30, 15, 14, 7, …
Cho K, A, B nhiệm vụ của bạn là đếm xem có bao nhiêu số nguyên X trong
đoạn từ A đến B mà dãy A[X] chứa K.
Dữ liệu vào: Trong file văn bản NEXT.INP Gồm một dòng duy nhất ghi 3
số K, A, B ( tất cả các số đều không âm và không quá 1018, A ≤ B).
Kết quả ra: Ra file văn bản NEXT.OUT Ghi ra kết quả tìm được.
Input

Output

348


2

1 23457 123456

100000

13 12345 67890123

8387584
Trang 21


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

Lời giải:

Nếu K = 0, kết quả là B – A + 1.

Gọi các số X mà trong A[X] có K là các số tạo K.
Trong trường hợp K >= 1. Các số tạo K sẽ có phần nhị phân đầu tiên giốn K
hoặc K + 1 trong trường hợp K chẵn.
Ví dụ nếu K = 1012 thì các số tạo K phải có dạng 101x 2 với x có thể rỗng
hoặc là một xâu nhị phân. Nếu K chẵn, ví dụ K = 100 2 thì các số tạo K có dạng
100x2 hoặc 101x2
Ta sẽ đếm các số này theo số lượng bit. Giả sử K có L bít, như vậy các số tạo
K có L bit sẽ nằm trong đoạn [C, D] với [C, D] bằng [K, K] nếu K lẻ và [K, K + 1]
nếu K chẵn. Các số tạo K có L + 1 bít sẽ nằm trong đoạn [C × 2, D × 2 + 1], các số
tạo K có L + 2 bít sẽ nằm trong đoạn [C × 2 × 2, (D × 2 + 1) × 2 + 1]. Tìm giao của
các đoạn này với đoạn [A, B] sẽ ra số lượng số cần tìm.

#include<bits/stdc++.h>
using namespace std;
#define bit(x, i)

(x >> i) & 1

void cass()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
freopen("next.inp", "r", stdin);
freopen("next.out", "w", stdout);
}
long long k, a, b;
int main()
{
cass();
cin >> k >> a >> b;
if (k==0) {cout << b - a + 1; return 0;}
long long l, r;
if (k & 1) l = r = k; else { l = k; r = k + 1;}
Trang 22


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

long long res = 0;
int cs1, cs2;
for (int i = 0; i < 64; ++i) {
if (bit(k, i)) cs1 = i;
if (bit(b, i)) cs2 = i;

}
int x=cs2-cs1;
while (x>=0){
long long tmp;
if (a > r || l > b) tmp = 0;
else {
long long mama = min(r, b), mimi = max(a, l);
tmp = max(mama - mimi + 1, 0LL);
}
res = res + tmp;
l = l * 2; r = r * 2 + 1;
x--;
}
cout << res;
}
Bài toán 11: Đầu bếp
Một đầu bếp đang chuẩn bị một bữa ăn với N nguyên liệu (N < 20). Vị đầu
bếp muốn bữa ăn phải có đầy đủ K chất (K < 20) C 1, C2, …, Ck. Để vấn đề dễ hiểu,
ta chưa quan tâm đến lượng của các chất trong bữa ăn. Với mỗi nguyên liệu ta biết
được nó có chứa chất nào trong thành phần của nó. Đầu bếp muốn biết tất cả các
tập nguyên liệu mà tập đó chứa đầy đủ K chất.
Dữ liệu:
Dòng 1: N và K

Trang 23


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài tốn Tin học

N dịng tiếp theo: mỗi dịng chứa K số thể hiện thành phần của mỗi nguyên

liệu, số thứ i thể hiện nó có chứa chất C i hay ko, là 1 nếu nó chứa chất C i, 0 ngược
lại.
Chẳng hạn:
67
1001010
0100100
1000001
0011000
1100000
0000010
Đầu bếp có 6 nguyên liệu, và ông ta quan tâm bữa ăn phải chứa đủ 7 chất.
Nguyên liệu thứ nhất chỉ chứa chất C1, C4, C6
Giải quyết:
Với mỗi nguyên liệu, nó sẽ có trạng thái là được chọn hoặc khơng được
chọn. Mỗi ngun liệu có 2 trạng thái, vậy sẽ có tất cả 2^N trạng thái cho N
nguyên liệu. Với những trường hợp N nhỏ thì duyệt qua tất cả các trạng thái là
chuyện dễ dàng (2^20 ~ 10^6). Cách duyệt qua tất cả các phương án để tìm lời giải
gọi là duyệt trâu bị. Vấn đề là thể hiện cách duyệt qua tất cả trạng thái bằng ngơn
ngữ lập trình. Bạn có thể dùng đệ qui để giải quyết, nó vẫn ok. Tuy nhiên, mình
muốn trình bày bằng bitmask, code ngắn, dễ sử dụng lại.
Do có 2^N trạng thái, tất cả trạng thái là 0000002=0, 0000012=1,
0000102=2, …, 1111112=2^N-1. Tất cả trạng thái là các số nguên chạy từ 0 đến
2^N-1. Nên chỉ với một vòng lặp for(state=0; state < 2^N; state++) đã biểu diễn
được việc “duyệt qua tất cả trạng thái“
Với mỗi giá trị của state, ta muốn biết nguyên liệu thứ i có được chọn hay
ko, ta chỉ cần xét biểu thức (state and 2^i), i tính từ 0 đến K-1. Nếu biểu thức này
lớn hơn 0 nghĩa là nguyên liệu thứ i được chọn trong trạng thái state, ngược lại thì
0. and ở đây là phép and bit.(Bạn tự kiểm chứng biểu thức này).
Một điểm chú ý nữa, Với mỗi nguyên liệu thứ i ta cũng chuyển thành một số
b[i] để diễn tả thành phần của nó. Ví dụ với ngun liệu thứ nhất, ta sẽ biểu diễn

b[0] = 10010102=74.
Trang 24


Kỹ thuật dùng bit trạng thái để xử lý hiệu quả bài toán Tin học

Như vậy nếu các nguyên liệu được chọn có các giá trị b[i] or với nhau hợp
thành 11111112=(2^K-1) thì tập đó thỏa mãn. or ở đây là phép or bit
For(state=0; state < 2^N; state++)
{
Sumarize = 0;
For(i=0; i < N; i++) if((state and 2^i) > 0)
{
Sumarize = Sumarize or b[i];
}
If(Sumarize == (2^K-1))
{
//Print the result
}
}
Độ phức tạp của đoạn code trên là O(2^N*K), với N=K=20 thì chạy trong 1s
là ok.
Bài toán 12: Biểu diễn trạng thái.
Với bài toán 10, chúng ta chỉ biểu diễn khơng hoặc có. Thực tế kĩ thuật này
được hiểu xa hơn một tí, ta xét ví dụ khá quen thuộc sau:
Ta có ba chai nước có thể tích là V 1, V2, V3 (Vi < 10) (đơn vị thể tích). Ban
đầu ta có chai nước thứ nhất đầy nước và hai chai còn lại khơng chứa gì cả. Bạn
cần tìm cách rót sao cho bình V3 chứa K (K<=V3) thể tích nước và số lần rót là ít
nhất. Bạn được thực hiện một trong các cách sau:
1. Rót hết nước từ chai này sang chai khác (được phép tràn)

2. Rót nước từ chai này sang chai khác cho đến lúc chai khác được nhận đầy
nước.
3. Rót bỏ hết nước từ một chai.
Với mỗi trạng thái (x1, x2, x3) thì ta cũng có thể dùng dãy 3 phần tử để diễn tả
chúng. Tuy nhiên ta cũng có thể diễn tả trạng thái của ba chai bằng một số nguyên
X= (x1*(V2+1)+x2)*(V3+1)+x3. Bạn có thắc mắc tại sao nhân với V2+1 và V3+1.
Trang 25


×