Bài toán ông sao Start
Trang 1Một ông sao sáng, hai ông sáng sao
Nguyễn Xuân Huy Bạn đếm có thạo không?
Chúng ta học đếm từ tấm bé nhưng hình như chưa thật thạo Nhiều khi chúng ta lúng túng không biết vận dụng các kỹ thuật đếm vào việc giải các bài toán tin học Bài này giới thiệu với bạn đọc một vài kỹ thuật đếm khá phổ biến trong tin học
Đếm theo phần
Người xưa đếm sao trên trời theo kiểu "Một ông sao sáng, hai ông sángsao " Đã bao giờ bạn thử tìm hiểu ý nghĩa của câu hát trên chưa? Xinmách bạn rằng, cho đến tận ngày nay, các nhà thiên văn vẫn áp dụng, phương pháp đơn giản này để đếm sao đấy Bản chất của
kỹ thuật này là như sau Ngườita chia các ngôi sao trên bầu trời theo từng nhóm căn cứ vào cấp độ sáng củachúng Các sao sáng nhất gọi là sao cấp 0, sau đó đến các sao cấp 1, cấp 2, Rồi người ta đếm theo từng nhóm Dĩ nhiên là bầu trời mênh mông, số lượng saolà vô hạn, đếm mãi cũng không xuể Ta đang nói đến việc đếm các ngôi sao nhìnthấy được bằng mắt thường Ta thử vận dụng kỹ thuật đếm theo nhóm để giải bàitoán-tin học sau đây
Bài 1 (Sơn cột, bài thi học sinh giỏiquốc gia, khối B) Trên mảnh đất hình chữ nhật kích
thước m x n ô vuông đơn vị,người ta xây các cột bê tông hình vuông tại các ô Mỗi cột bao gồm một số khốilập phương đơn vị chồng lên nhau Các cột có thể cao thấp khác nhau Sau đóngười ta sơn các mặt bê tông nhìn thấy được của các cột Tính diện tích cầnsơn
Dữ liệu vào: Ghi trong tệp văn bản COT.INP, dòng đầu tiên là hai giá trị m và n.Tiếp theo đó là m dòng, mỗi dòng có n số tự nhiên biểu thị chiều cao đo bằng sốlượng khối lập phương tạo nên cột bê tông tại vị trí đó
m, n và chiều cao của mỗi cột không quá 100
Dữ liệu ra: Hiển thị trên màn hình diện tích cần sơn tính theo ô vuông đơn vị
Thí dụ, với m=3, n=4 và tệp COT.INP có dạng sau:
COT.INP
3 4
1 0 3 1
Trang 22 2 0 1
0 0 1 4
Thì sau khi thực hiện chương trình trên màn hình phải xuất hiện giá trị
5 4
Lời giải
Gọi mảnh đất hình chữ nhật là ABCD Ta đếm từng mặt theo trật tự tuỳ ý, chẳng hạn:
1 Đếm số khối trên hai chiều dài AB và DC
2 Đếm số khối trên hai chiều rộng AD và BC
3 Đếm số khối nhìn thấy theo chiều ngang giữa các hàng
4 Đếm số khối nhìn thấy theo chiều dọc giữa các cột
5 Đếm số khối trên mặt
Trong chương trình SONCOT.PAS dưới đây thủ tục DOS sẽ đọc dữ liệu từ tệp COT.INT vào mảng a Thủ tục Xem hiển thị giá trị của mảng a trên màn hình Thủ tục Dem thực hiện giải thuật 5 bước nói trên
Chương trình Pascal
(*SONCOT.PAS*)
uses crt;
const fn = 'COT.INP'; MN=100;
var f:text;
a: array[1 MN, 1 MN] of byte;
{chua du lieu doc tu tep}
d: longint; {dien tich can so}
m,n: integer; {chieu dai va rong cua manh dat}
{Doc du lieu tu tep vao mang a}
Trang 3procedure DocTep;
var i, j: byte;
begin
assign(f,fn);
reset(f);
read(f,m,n);
for i:=1 to m do
for j:=1 to n do read(f,a[i,j]);
close(f);
end;
{Hien thi du lieu tren man hinh de kiem tra} procedure Xem;
var i,j:integer;
begin
writeln('m= ',m, 'n= ',n);
for i:=1 to m do begin writeln;
for j:=1 to n do write(a[i,j]:4);
end;writeln;
end;
{Dem dien tich can so}
procedure Dem;
var i,j: byte;
begin
Trang 4{Dem theo 2 chieu dai AB và CD}
for j:=1 to n do d:=d+a[1,j]+a[m,j]
{Dem theo 2 chieu rong AD va BC}
fori:=1 to m do d:=d+a[i,1]+a[i,n]
{Dem so khoi nhin thay theo chieu ngang giua cac hang} fori:=2 to m do
for j:=1 to n do
d:=d+abs(a[i,j]-a[i-1,j]);
{Dem so khoi nhin thay theo chieu doc giua cac cot} forj:=2 to n do
for i:=1 to m do
d:=d+abs(a[i,j]-a[i,j-1]);
{Dem mat tren}
fori:=1 to m do
for j:=1 to n do
if a[i,j]>0 then d:=d+1;
end;
BEGIN
DocTep;
Xem;
Dem;
writeln(d);
Trang 5Du lieu test:
COT.INP
3 4
1 0 3 1
2 2 0 1
0 0 1 4
Ket qua du kien: 5 4
Dưới đây là chương trình viết bằng ngôn ngữ C trong môi trường Turbor C hoặc C++
Chương trình C
/*SONCOT.C*/
#include
#include
#include
#define fn "COT.INP"
#define MN 100
#define byte unsigned char
void DocTep();
void Xem();
void Dem();
FILE *f;
byte a[MN][MN];
long d; /*Dien tich can so*/
Trang 6int m,n;
main()
{clrscr();
DocTep();
Xem();
Dem();
printf("%d",d);
getch();
}
/*Doc du lieu tu tep vao mang a */
void DocTep()
{byte i,j;
f=fopen(fn,"rt");
fscanf(f, "%d", &m, &n);
for (i=0; i<>
for (j=0; j<>
fscanf(f, "%d", &a[i][j]);
fclose(f);
}
/* Hien thi du lieu tren man hinh de kiem tra */ void Xem()
{byte i,j;
prinf("m=%d n=%d n",m,n);
Trang 7for (i=0;i<M;++I)< p> {printf("n");
for (j=0;j<N;++J)< p> printf("%4d", a[i][j]); }
printf("n");
}
/*Dem dien tich can son */ voidDem()
{byte i,j;
d=0;
for(j=0;j<N;++J)< p> d+=a[0][j]+a[m-1][j]; for(i=0;i<M;++I)< p> d+=a[i][0]+a[i][n-1]; for(i=1;i<M;++I)< p> for(j=0;j<N;++J)< p> d+= abs(a[i][j]-a[i-1][j]); for(j=1;j<N;++J)< p>
? for(i=0;i<M;++I)< p> d+=abs(a[i][j]-a[i][j-1]); for(i=0;i<M;++I)< p> for(j=0;j<N;++J)< p>
Trang 8d++;
}
Ketqua du kien: 5 4
Duyệtmột lần, ghi theo loại
Đểkiểm phiếu người ta thường đọc một lần các lá phiếu Với mỗi lá phiếu người tachọn
và ghi thêm một đơn vị cho mỗi đại biểu được bầu Chúng ta sẽ vận dụng kỹthuật này để giải bài toán thoạt xem tưởng như khó sau đây
Bài2 Tệp văn bản DAYSO.INP chứa nhiềudẫy số Mỗi dẫy số gồm N giá trị nguyên
a1,a2, ,aN, 2< N< 2000 Hãy viếthàm DangDieu kiểm tra dáng điệu của các giá trị trong mỗi dãy Hàm cho ra cácgiá trị nhận biết sau đây:
1: Nếu dãy đã cho là đồng nhất, a1=a2= =aN
2: Nếu dãy đã cho là tăng chặt, a1 > a2 > > aN
3: Nếu dãy đã cho là đồng biến, a1 ≥ a2 ≥ ≥ aN
4: Nếu dãy đã cho là giảm chặt, a1 < a2 < < aN
5: Nếu dãy đã cho là nghịch biến, a1 ≤ a2 ≤ ≤ aN
0: Nếu không xảy ra các trường hợp 1 5
Lời giải
Ta lần lượt đọc và so sánh hai phần tử đứng liên tiếp nhau vào hai biến tương ứng là asau
và atruoc
- Nếu asau=atruoc ta tăng con đếm b
- Nếu asau > atruoc ta tăng con đếm t
-Nếu asau < atruoc ta tặng con đếm g
Nói một cách hình ảnh, ta sử dụng ba cái lọ b và t và g Lọ b dùng để đếm các cặp phần
tử bằng nhau, lọ t đếm các cặp phần tử tăng và lọ g đếm các cặp phần tử giảm Ta mang theo ba lọ b,t và g và một nắm hạt ngô rồi đi dạo trên dãy số, xuất phát từ phần tử đầu tiên là a1 Mỗi lần bước sang phần tử tiếp theo ta so sánh phần tử đó với phần tử trước Nếu hai phần tử bằng nhau thì bỏ một hạt ngô vào lọ b Nếu phần tử sau lớn hơn phần tử
Trang 9trước thì bỏ một hạt ngô vào lọ t Nếu phần tử sau nhỏ hơn phần tử trước ta bỏ một hạt ngô vào lọ g Tổng cộng ta bước N-1 bước, mỗi bước sử dụng đúng một hạt ngô để bỏ vào một trong ba lọ Vậy ta có: b+t+g=N-1 Hệ thức này gợi ý cho ta chỉ sử dụng hai thay
vì ba lọ Tachọn b và t
Saukhi đến đích ta tính giá trị g theo công thức g=N-1-t-b rồi chỉ cần lắc các lọlà có thể biết được dáng điệu của dãy đã cho
Nếulọ kêu, tức là trong lọ có ngô ta ghi 1, ngược lại, nếu lọ rỗng ta ghi 0 Giátrị nh phân thu được của ba lọ xếp theo thứ tự gtb sẽ cho dáng điệu của dãy số.Để ý hai trường hợp cuối cùng 110 và 111 ứng với các giá trị 6 và 7 Các trườnghợp này cho biết dãy vừa có khúc tăng vừa có khúc giảm (Xem bảng dưới)
Bảng1 Lắc các lọ g, t và b ta có thể xácđịnh dáng điệu của dãy a.
Lọkêu: 1, lọ không kêu: 0; Cột dáng điệu cho ra giá trị thập phân, trong ngoặc làgiá trị nhị phân tương ứng
Chươngtrình dưới đây đọc dữ liệu từ tệp văn bản DAYSO.INP nhiều dãy số vào mảng a rồixác định dáng điệu của dãy số đó và hiển thị kết quả trên màn hình Để kiểm
thửchương trình bạn cần tạo trước tệp văn bản DAYSO.INP và nạp các dãy số Với mỗidãy cần có một số N cho biết số lượng các phần tử trong dãy đó tiếp đến là N sốtrong dãy Tệp kiểm thử DAYSO.INP dưới đây chứa 6 dãy với các dáng điệu khácnhau
{DANGDIEU.PAS}
Trang 10(*Chuy: Ban can tao truoc tep du lieu Test DAYSO.INP *) usescrt;
constfn='DAYSO.INP'; MN=2000;
varf:text;
a: array [0 MN] of integer;
{Du lieu trong day}
n: integer;
{So luong phan tu trong moi day}
{Hien thi du moi day}
procedureXemDuLieu;
vari:integer;
begin
writeln; write(' N= ',n); writeln;
for i:=1 to n do write(a[i]:4); writeln;
end;
functionDangDieu: integer;
varg, t, b, i: integer;
{ba lo g, t, b va con duyet i}
begin
t:=0, b:=0;
for i:=2 to n do
if(a[i]=a[i-1]) then inc(b)
else if(a[i]>a[i-1]) then inc(t);
Trang 11if g>0 then g:=1;
if t >0 then t:=1;
if b>0 then b:=1;
i:=4*g+2*t+b;
if i>5 then DangDieu:=0
else DangDieu:=i;
end;
procedureRun;
vari: integer;
begin
assign(f,fn);
reset(f);
whilenot eof(f) do begin
read(f,n);
ifn=0 then begin close(f); exit; end; fori:=1 to n do read(f,a[i]);
XemDuLieu;
write('DangDieu=',DangDieu,':'); case DangDieu of
0: writeln('Tang giam');
1: writeln('Dong nhat');
2: writeln('Tang chat');
Trang 123: writeln('Dong bien');
4: writeln('Giam chat');
5: writeln('Nghich bien');
end;end;
end;
BEGIN
Run;
readln;
END
Dulieu Test gom 6 day so co dang dieu khac nhau: DAYSO.INP
5
3 3 3 3 3
7
1 40 50 60 77 100 102
8
1 3 3 9 9 9 40 105
7
100 80 77 14 10 5 1
7
100 80 80 14 10 5 1
7
19 9 3 2 2 1
Trang 13/*DANGDIEU.C */
/*Chuy: Ban can tao truoc tep du lieu test SAP.INP */
#include
#include
#include
#include
#definefn "DAYSO.INP"
#defineMN 2000
#definebyte unsigned char
/*Thiet ke */
voidXemDuLieu();
intDangDieu();
voidRun();
FILE*f;
inta[MN]; /* Chua du lieu cua moi day */
intn; /* So phan tu trong moi day */
main()
{Run();
getch();
}
/*Hien thi mot day */
voidXemDuLieu()
{int i;
Trang 14printf("n N=%d n",n);
for(i=0;i<N;++I)< p>
printf("%4d", a[i]);
printf("n");
}
intDangDieu()
{int g, t, b, i; /* Ba lo g, t, b va con duyet i */ t=b=0;
for(i=1;i<N;++I)< p>
if(a[i]==a[i-1]) ++b;
??else if(a[i]>a[i-1]) ++t;
g=n-1-t-b;
if (g>0)
g=1;
if (t>0)
t=1;
if (b>0)
b=1;
i=4*g+2*t+b;
return (i>5)? 0:i;
}
voidRun()
{ int i;
Trang 15f=fopen(fn,"r");
while (!feof(f))
{ fscanf(f,"%d",&n);
if(n==0)
{ fclose(f);
return;
}
for(i=0;i<N;++I)< p>
fscanf(f,"%d",&a[i]);
fscanf(f,"n");
XemDuLieu();
i=DangDieu();
printf("DangDieu=%d: ",i);
switch(i)
{ case 0: printf("Tang giam"); break; case 1: printf("Dong nhat"); break; case 2: printf("Tang chat"); break; case 3: printf("Dong bien"); break; case 4: printf("Giam chat"); break; case 5: printf("Nghich bien"); break; }
}
Trang 16}