Chuyên đề:Một số bài toán quy hoạch động thiên về công “Một số bài toán quy hoạch động thiên về công thức toán “Một số bài toán quy hoạch động thiên về công... Một số bài toán quy hoạch
Trang 1Chuyên đề:
Một số bài toán quy hoạch động thiên về công
“Một số bài toán quy hoạch động thiên về công
thức toán “Một số bài toán quy hoạch động thiên về công
Trang 2Một số bài toán quy hoạch động thiên về công thức toán
“Một số bài toán quy hoạch động thiên về công “Một số bài toán quy hoạch động thiên về công
I/ Đặt vấn đề.
Về quy hoạch động đã có quá nhiều tài liệu, chuyên đề nói đến, ở đây chúng ta chỉ xét một số bài tập khi giảI quyết nó nếu suy nghĩ theo hớng toán học sẽ thuận lợi hơn (tất nhiên vẫn có thể giảI theo nhiều cách)
II Bài tập.
Bài 1 Đếm chữ số Tên chơng trình DEMCHUSO.???
Cho 2 số nguyên a và b (0 ≤ a ,b ≤ 1000 000 000) Xét tất cả các số nguyên nằm giữa a va b (kể cả 2 số này)
Hãy đếm số lợng từng chữ số thập phân xuất hiện trong tất cả các số đó
Ví dụ, với a = 1024 và b = 1032, ta có dãy số nguyên:
1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032
Trong dãy này có 10 chữ số 0, 10 chữ số 1, 7 chữ số 2, 3 chữ số 3, 1 chữ số 4, 1 chữ số 5, 1 chữ số 6, 1 chữ số 7, 1 chữ số 8, 1 chữ số 9
Dữ liệu: vào từ file văn bản DEMCHUSO.INP gồm nhiều dòng (nhng không quá
5000) Mỗi dòng ứng với 1 test chứa 2 số nguyên a và b
Kết quả: Đa ra file văn bản DEMCHUSO.OUT, kết quả mỗi test trên một dòng,
gồm 10 số nguyên cho biết số lợng các chữ số 0, 1, 2, ,9 Giữa 2 số cách nhau
ít nhát 1 dấu khoảng trống
Ví dụ:
1 10
44 497
346 542
1004 502
1 2 1 1 1 1 1 1 1 1
85 185 185 185 190 96 96 96 95 93
40 40 40 93 136 82 40 40 40 40
107 105 100 101 101 197 200 200 200 200
Bài 2 Viết số Tên chơng trình VIETSO.???
cách viết các số từ 1 đến 2N vào lới, mỗi số 1 ô, số i và số i + 1 đợc viết vào 2 ô
kề cạnh
Dữ liệu: Vào từ file VIETSO.INP gồm duy nhất 1 dòng chứa số nguyên N.
Kết quả: đa ra VIETSO.OUT số cách viết.
Ví dụ
Trang 3Bài 3 Qua cầu Tên chơng trình CROSS.???
(Bài 6 thi HSG Quốc gia năm 2012)
Một nhóm n bạn đi tập văn nghệ về khuya Cả nhóm chỉ có một chiếc đèn pin và phải qua một cây cầu gồm m đoạn, các đoạn đợc đánh số từ 1 đến m kể từ bờ
đang đứng sang bờ bên kia Coi vị trí n bạn đang đứng là đoạn thứ 0 và đầu cầu bên kia là đoạn thứ m +1 Cây cầu đã cũ, do đó có 1 số đoạn cầu đã hỏng và không thể đi vào đợc, hơn nữa cầu chỉ chịu đựng đợc sức nặng của không quá hai ngời Để qua cầu an toàn các bạn phải tổ chức qua cầu theo cách sau: mỗi lợt chỉ
có hai ngời cầm đèn pin để cùng nhau qua cầu và không đợc đi vào những vị trí
đoạn cầu đã bị hỏng Sau khi hai ngời qua đến đầu cầu bên kia thi những ngời đã qua cầu phải cử một ngời đem đèn pin trở lại đầu cầu bên này để các bạn khác
nghĩa là nếu bạn thứ i đang đứng ở đoạn thứ s của cây cầu thì bạn có thể di
Việc thực hiện một bớc đi đòi hỏi 1 đơn vị thời gian Do đó có thể có ngời qua cầu nhanh hơn Nếu hai bạn cùng qua cầu thì họ phải di chuyển qua cầu với thời gian của bạn chậm hơn Vì đã qua khuya nên cả nhóm bàn nhau tim cách qua cầu sớm nhất có thể đợc
Yêu cầu: Cho biết vị trí các đoạn càu bị hỏng và khả năng di chuyển của từng
Dữ liệu: Vào từ file văn ban CROSS.INP:
Dòng thứ nhất chứa hai số nguyên dơng n và m tơng ứng là số bạn trong nhóm và số đoạn của cây cầu ( n ≤ 10000, m ≤100000);
Dòng th 3 cha một xâu gồm m ký tự ‘0’ hoặc ‘1’ mô tả trạng thái của cây cầu Ký tự thứ i của xâu là ‘0’ nếu đoạn thứ i không bị hỏng có thể đi vào
đợc, là ‘1’ nếu đoạn cầu th i bị hỏng không thể đi vào đợc
Các số trên cùng một dòng đợc ghi cách nhau ít nhất một dấu cách
Kết quả : ghi ra file văn bản CROSS.OUT một số nguyên là khoảng cách thời gian ngắn nhất để n bạn vợt qua đợc cây cầu
Ví dụ:
3 5
2 2 4
00100
8
Trang 4III Phân tích
Bài 1 Gọi d[i] là số chữ số i cần tìm (i = 0 9)
Cách thứ nhất: cách tự nhiên nhất là xét tất cả các số từ a đến b (trong trờng hợp a
> b) ta chỉ việc đổi 2 giá trị cho nhau nh ở thủ tục tinhd dới đây), với mỗi số ta lại xét từng chữ số của nó
Ta có chơng trình con tính mảng d nh sau:
procedure tinhd;
var i,j:longint;
k:integer;
begin
for i:=0 to 9 do do d[i]=0;
if a>b then
begin
j:=a; a:=b; b:=j;
end;
for i:=a to b do
begin
J:=i;
while j > 0 do
begin
k := j mod 10;
inc(d[k]);
J:=j div 10;
end;
end;
end;
Cách này cực kỳ đơn giản, nhng độ phức tạp của thuật toán là O(số dòng x b x số
Cách thứ 2: Giả thiết a ≤ b Nh cách 1 ta luôn có thể đảm bảo đợc điều này
Gọi fa[i] là số chữ số i của các số 1, 2, …a - 1
Gọi fb[i] là số chữ số i của các số 1, 2, …b
Gọi kq[i] là số chữ số i của các số a,a+1, …,b
(i = 0 9)
Ta sẽ có kq[i]:=fb[i]-fa[i];
Để tính số chữ số i khi viết các số t 1 đến x ta xhỉ cần xét x
Gọi FX[i] là số chữ số i khi viết 1, 2, …,x, ta có:
FX[i] = Số chữ số i ở hàng đơn vị của các số 1, 2, …x
+ Số chữ số i ở hàng chục của các số 1, 2, …,x
+ Số chữ số i ở hàng trăm của các số 1, 2, …,x
Ta thử xét chữ số i ở hàng đơn vị:
Số chữ số 0 sẽ là s vì ta có 10, 20, …s0
Trang 5Chữ số x0+1 đến 9 là s.
Ta hãy tính số các chữ số ở vị trí j tổng quát
Ta có thể tổng quát cả trờng hợp chữ số hàng đơn vị
Ta có thủ tục tinh mảng fx nh sau:
procedure tinhf(x:longint,var fx:mcso);
var s,q,t:longint;
i, k:integer;
begin
for i := 0 to 9 do fx[i] := 0;
t:=1;
q:=0;
s :=x;
while s > 0 do
begin
k:=s mod 10;
s:=s div 10;
If k > 0 then fx[0]:=fx[0] + s*t+q else fx[0]:=fx[0]+ (s-1)*t+q +1; for i:=1 to k-1 do fx[i]:=fx[i]+(s+1)*t;
ì k > 0 then fx[k]:=fx[k] + s*t+q + 1;
for i:=k+1 to 9 do fx[i]:=fx[i] + s*t;
q: = k*t + q;
t:=t *10;
end;
end;
Chơng trình chính
uses crt;
type mcso=array[0 9] ò int64;
const fi =’demchuso.inp’;
fo =’demchuso.out’;
Var a,b,j:longint;
i:integer;
fa,fb:mcso;
f1,e2:text;
procedure mofile;
begin
assign(f1,fi);
reset(f1);
assign(f2,fo);
rewrite(f2);
end;
procedure tinhf(x:longint,var fx:mcso);
var s,q,t:longint;
i, k:integer;
begin
Trang 6for i := 0 to 9 do fx[i] := 0;
t:=1;
q:=0;
s :=x;
while s > 0 do
begin
k:=s mod 10;
s:=s div 10;
If k > 0 then fx[0]:=fx[0] + s*t+q else fx[0]:=fx[0]+ (s-1)*t+q +1; for i:=1 to k-1 do fx[i]:=fx[i]+(s+1)*t;
× k > 0 then fx[k]:=fx[k] + s*t+q + 1;
for i:=k+1 to 9 do fx[i]:=fx[i] + s*t;
q: = k*t + q;
t:=t *10;
end;
end;
Begin
clrscr;
mofile;
while not eof(f1) do
begin
readln(f1,a,b);
if a>b then
begin
j:=a; a:=b; b:=j;
end;
tinhf(a-1,fa);
tinhf(b,fb);
for i:=0 to 9 do write(f2,fb[i]-fa[i],’ ‘);
writeln(f2);
end;
close(f1); close(f2);
End
Trang 7Bài 2 Để tính số cách viêt các số từ 1 đến 2N chúng ta có thể thử viết số 1 vào 1
ô, sau đó viết các số còn lại
+ Viết 2 vào ô (1,2): có duy nhất 1 cách viết
+ Viết 2 vào ô (2,1), lúc này 3 phải viết vào ô (2,2) Bài toán trở thành : tìm số cách viết 2(n-1) số Trong trờng hợp này ta có công thức FG(n) = 1 + FG(n-1) Có thể dung hàm đệ quy để tinh FG(n) Nhng nếu xét thêm 1 chút nữa ta dễ dàng suy
ra FG(n) = n Với n ≥ 2 ta có 4 ô góc
1
+ 2 viết vào ô bên phải : ứng dụng kết quả phía trên số cách viết sẽ là j-1
+ 2 viết vào ô bên trái : ứng dụng kết quả phía trên số cách viết sẽ là n-j
Nh vậy số cách viết trong trờng hớp này là: n-j+j-1 = n-1
Với 1 viết vào ô (2,j) ta đợc kết quả hoàn toàn tơng tự
Tổng hợp lại ta có 4 ô góc, 2(n-2) ô không ở góc:
Kết quả sẽ là: 4n + 2(n-2)(n-1) (*) cách viết
Hàm F(x) sẽ cho ta số cách viết các số từ 1 đến 2x vào lới vuông 2 dòng, x cột mà
số i và số i+1 kề cạnh
function f(x:longint):int64;
var y:int64;
begin
if x=1 then exit(2);
y:=x;
y:=4*y+2*(y-2)*(y-1);
exit(y);
End;
{với x = 1 là trờng hợp đặc biệt, đợc tính riêng, còn x = 2 mặc du chỉ có 4 ô góc, công thức (*) vẫn đúng}
Trang 8Ch¬ng tr×nh chÝnh
uses crt;
const fi =’vietso.inp’;
fo =’viet.out’;
Var n:longint;
f1,e2:text;
procedure mofile;
begin
assign(f1,fi);
reset(f1);
assign(f2,fo);
rewrite(f2);
end;
function f(x:longint):int64;
var y:int64;
begin
if x=1 then exit(2);
y:=x;
y:=4*y+2*(y-2)*(y-1);
exit(y);
End;
Begin
clrscr;
mofile;
read(f1,n);
writeln(f1,f(n));
close(f1); close(f2);
end
Trang 9Bài 3.
Nhận xét: để giải quyết bài toán này chúng ta phải tiến hành qua 2 bớc.
Bớc 1: Tính thời gian qua cầu nhanh nhất cho mỗi bạn.
Bớc 2 : Tính thời gian qua cầu nhanh nhất cho cả nhóm.
Nh vậy ta có thể tách bài toán này thành 2 bài toán nhỏ:
Bài toán 1: Cho cây cầu có độ dài m, một ngời có bớc dài r, mỗi đơn vị thời gian
ngời đó có thể bớc không quá r đoạn, nghĩa là anh ta đang ở vị trí s thì có thể bớc tới các vị trí s+1, s+2, …,s+r nếu đoạn đó không bị hỏng Bài toán 1 dễ dàng đợc giải quyết bằng quy hoạch động với hàm tính thời gian nh sau:
f(r) cho ta thời gian nhanh nhất qua cầu của mọt bạn có bớc nhảy là r
function f(r:integer):longint;
var d:aray[-100 100001] of longint;
i,j:longint;
begin
for i:=1 to m+1 do d[i]:=100001;
for i:=-100 to 0 do d[i]:=0;
for i:=1 to m+1 do
if x[i]=0 then
for j:=i-r to i-1 do
if d[i] > d[j]+1 then d[i]:=d[j]+1;
f:=d[m+1];
end;
Sau khi giải quyết xong bài toán 1, ta dễ dàng tính thời gian cho tất cả các bạn bằng cách áp dụng n lần sử dụng hàm f
Trong trờng hợp này độ phức tạp của thuật toán sẽ là O(n.m.r) là quá lớn
Ta lại có thể cải tiến hàm f nh sau: Độ phức tạp là O(m)
function f(r:integer):longint;
var d:aray[-100 100001] of longint;
i,j,v0,v1:longint;
begin
for i:=1 to m+1 do d[i]:=100001;
for i:=-100 to 0 do d[i]:=0;
v0:=0;v1:=0;
for i:=1 to m+1 do
if x[i]=0 then
if v0+r > =i then begin d[i]:=d[v0]+1;v1:=i;end
else begin d[i]:=d[v1] + 1;v0:=v1;v1:=i; end;
f:=d[m+1];
End;
Trong đó v0 là vị trí xa nhất của bớc trớc đó V1 là vị trí gần i nhất đã đợc bớc tới
Để giải quyết bài toán 2 dễ dàng ta còn phải tính thời gian cho các nhóm, sắp xếp chúng theo thứ tự tăng dần: việc này có thể dễ dàng giải quyết với suy luận bạn
Ta có 3 mảng sau:
Trang 10sn[1 100] : s[r] số bạn có bớc nhảy r
t[1 100] :t[r] thời gian qua cầu với nhanh nhất với bớc nhảy là r
tg[1 10000] of longint tg[i] thời gian qua cầu của bạn nhanh thứ i;
procedure nhapdl;
var f:text;
i,r:integer;
begin
fillchar(sn,sizèo(sn),0);
assgn(f,fi);
readln(f,n,m);
for i:=1 to m do
begin
read(f,r);
sn[r]:=sn[r]+1;
end;
readln(f);
readln(f,x);
close(f);
end;
procedure tinhtg;
var ig,k:integer;
begin
for ig:=1 to 100 do if sn[ig]>0 then t[ig]:=f[ig];
k:=0;
for ig:=100 downto 1 do
for jg:=1 to sn[ig] do
begin
k:=k+1;
tg[k]:=t[ig];
end;
End;
Bài toán 2: Để qua cầu nhanh nhất xét theo quan điểm toán học ta có 2 sự u tien
sau:
trong các bạn đã qua cầu
hoặc đi với ngời nhanh nhất (để giảm thời gian về đón)
Ta có thể giải quyết riêng các trờng đặc biệt
n = 1, kq = tg[1]
n = 2, kq = tg[2]
n = 3, kq = tg[1]+tg[2]+tg[3]
Ta xét với n = 4
Ta ký hiệu (i,j) là bạn i và bạn j qua cầu (i) bạn i quay lại
Phơng án 1 theo u tiên trên (1,2) (1) (3,4)(2)(1,2)
Thời gian qua cầu của nhóm là T1 = tg[2]+tg[1]+tg[4]+tg[2]+tg[2];
(tg[3] đợc ẩn đi)
Phơng án 2 theo u tiên trên (1,2) (1) (1,3)(1)(1,4)
Thời gian qua cầu của nhóm là T2 = tg[2]+tg[1]+tg[3]+tg[1]+tg[4];
Xét T1-T2 = 2tg[2] – (tg[1] + tg[3])
Trang 11Dễ thấy nếu 2tg[2] < tg[1] + tg[3] đi theo phơng án 1 lợi hơn, ngợc lại đi theo phơng án hai lợi hơn
Mở rộng cho n bạn (đã sắp xếp theo thời gian tăng dần)
Hàm tính kết quả nh sau:
function tinhkq:longint;
var k,i:integer;
begin
if n=1 then exit(tg[1]);
kq:=0;
k:=n;
while (k>3) and (2tg[2] < tg[1] + tg[k-1]) do
begin
kq:=kq+tg[2]; {(1,2)}
kq:=kq+tg[1]; {(1)}
kq:=kq+tg[k]; {(k-1,k)}
kq:=kq+tg[2] ; {(2)}
k:=k-2;
End;
for i:=2 to k do kq:=kq+tg[i] ; {(1,i)}
kq:=kq+(k-2)*tg[1];
exit(kq);
end;
Đánh giá độ phức tạp: Nhập dữ liệu O(n+m)
Tính thời nhan theo các nhóm O(m.r)
Tính mảng tg O(n)
Tính kết quả O(n)
Do nmax khá nhỏ so với mmax :Đánh giá chung O(m.r)
Chơng trình chính
uses crt;
const fi =’cross.inp’;
fo =’ cross.out’;
Var f:text;
n:integer;
m:longint;
sn:array[1 100] of integer;
t:array[1 100] of longint;
tg:aray[1 10000] of longint;
procedure nhapdl;
var f:text;
i,r:integer;
begin
fillchar(sn,sizèo(sn),0);
assgn(f,fi);
readln(f,n,m);
for i:=1 to m do
begin
read(f,r);
sn[r]:=sn[r]+1;
Trang 12end;
readln(f);
readln(f,x);
close(f);
end;
function f(r:integer):longint;
var d:aray[-100 100001] of longint;
i,j,v0,v1:longint;
begin
for i:=1 to m+1 do d[i]:=100001;
for i:=-100 to 0 do d[i]:=0;
v0:=0;v1:=0;
for i:=1 to m+1 do
if x[i]=0 then
if v0+r > =i then begin d[i]:=d[v0]+1;v1:=i;end else begin d[i]:=d[v1] + 1;v0:=v1;v1:=i; end; f:=d[m+1];
End;
procedure tinhtg;
var ig,k:integer;
begin
for ig:=1 to 100 do if sn[ig]>0 then t[ig]:=f(ig); k:=0;
for ig:=100 downto 1 do
for jg:=1 to sn[ig] do
begin
k:=k+1;
tg[k]:=t[ig];
end;
end;
function tinhkq:longint;
var k,i:integer;
begin
if n=1 then exit(tg[1]);
kq:=0;
k:=n;
while (k>3) and (2tg[2] < tg[1] + tg[k-1]) do
begin
kq:=kq+tg[2]; {(1,2)}
kq:=kq+tg[1]; {(1)}
kq:=kq+tg[k]; {(k-1,k)}
kq:=kq+tg[2] ; {(2)}
k:=k-2;
end;
for i:=2 to k do kq:=kq+tg[i] ; {(1,i)}
kq:=kq+(k-2)*tg[1];
exit(kq);
Trang 13procedure inkq;
var g:text;
begin;
assign(g,fo);
rewrite(g);
writeln(g,tinhkq);
close(g);
end;
Begin
clrscr;
nhapdl;
tinhtg;
inkq;
end