Một số thuật toán, bài toán hay thường gặp

M

mikelhpdatke

[TẶNG BẠN] TRỌN BỘ Bí kíp học tốt 08 môn
Chắc suất Đại học top - Giữ chỗ ngay!!

ĐĂNG BÀI NGAY để cùng trao đổi với các thành viên siêu nhiệt tình & dễ thương trên diễn đàn.

Topic sẽ tổng hợp những bài toán hay, thuật toán hấp dẫn chúng ta thường gặp , những bài toán cơ bản cho newbie đang học thuật


 
Last edited by a moderator:
M

mikelhpdatke

Sàng nguyên tố

#2:Sàng nguyên tố

#Wikipedia:

Khi cần biết các số nguyên tố đến một phạm vi nào đó, ví dụ từ 2 đến 108, sử dụng sàng số nguyên tố Eratosthenes sẽ hiệu qủa hơn về thời gian.
Thủ tục sau tạo sàng số nguyên tố từ 2 đến N:



B1: Ta tìm các số chia hết cho 2, ngoại trừ chính nó.

B2: Tìm các số chia hết cho 3, cũng không tính 3.

B3: Tìm các số chia hết cho 5, trừ 5.

B4:Ta tìm các số chia hết cho 7, trừ 7.

B5: Chọn ra các số còn lại, chúng chính là số nguyên tố.

Ideone

Mã:
procedure sieve(n:integer);
 begin
      fillchar(p, sizeof(p), true);
      for i:=2 to trunc(sqrt(n)) do
           if (p[i]) then
 begin
      j:=i+i;
      while (j<=n) do
      begin
           p[j]:=false;
           j:=j+i;
      end; 
 end;
 end;
Thông tin trả về trong mảng p: p = true nếu và chỉ nếu i là số nguyên tố.
Bạn có thể ước lượng thời gian chạy của thuật toán sàng Eratosthenes là O(nlogn), ta sẽ đề cập đến một số ước lượng cần thiết ở những phần sau.
 
Last edited by a moderator:
M

mikelhpdatke

Liệt kê dãy nhị phân độ dài n

#3: Liệt kê dãy nhị phân độ dài n


Biểu diễn dãy nhị phân độ dài N dưới dạng $(x_1, x_2, ..., x_n)$. Ta sẽ luệt kê các dãy này bằng cách thử từng giá trị $(0, 1)$ gán cho $x_i$. Với mỗi giá trị thử gán cho $x_i$ lại thử các giá trị có thể gán cho $x_{i+1}$. * Trích DASP :D*

Thuật làm dưới đây là thuật toán quay lui

Mã:
Program Liet_ke_nhi_phan;

Const max=100;


Var A: Array[1..max] Of Integer;
    N: Integer;


Procedure Init;
Var i:integer;
 Begin

     Write('n=');
     Readln(n);


 End;

Procedure PrintReSult;
Var i:integer;
 Begin
   For i:=1 to n do write(A[i]);
   Writeln;
 End;


Procedure Try(i:integer);
Var j:Integer;
 Begin
    For j:=0 to 1 do
      Begin
         A[i]:=j;
         If i=n then PrintReSult
                        Else Try(i+1);
      End;

End;


BEGIN
Init;
Try(1);
Readln
END.


Ứng dụng:
Từ bài toán này, ta có thể áp dụng cách này để giải quyết "trâu bò" nhiều bài toán khác
VD: với $A=1$ thì chọn cấu hình thứ i. $A=0$ thì ngược lại



 
Last edited by a moderator:
M

mikelhpdatke

Liệt kê tập con K phần tử

#4 Liệt kê tập con K phần tử




Để liệt kê tập con $k$ phần tử của tập $S={1, 2,..., n}$ ta có thể đưa về liệt kê các cấu hình ${x_1, x_2, x_3,...., x_k}$ với $x_i \in S$ và $x_1<x_2<...<x_k$. Ta có nhận xét:

[TEX] \ \ \ \ \ x_{k} \leq n \\ x_{k-1} \leq x_k-1 \leq n-1\\ x_{i} \leq n-k+i\\ x_{1} \leq n-k+1[/TEX]


Từ đó suy ra [TEX]x_{i-1}+1 \leq x_i \leq n-k+i[/TEX] với [TEX] (1 \leq i \leq k)[/TEX]. Ở đây ta giả thiết có thêm $x_0=0$ khi xét i=1.

Như vậy ta sẽ xét tất cả các cách chọn từ $x_1$ từ $1$ đến $n-k+1$, với mỗi giá trị đó, xét tất cả cách cách chọn từ $x_2$ đến $n-k+2$,.. cứ như vậy khi chọn được đến $x_k$ thì ta có mỗi cấu hình cần liệt kê. * DASP *
Chương trình có cấu trúc giống bài liệt kê dãy nhị phân độ dài k ở #3, chỉ thay vòng for ở thủ tục Try(i) thành
Mã:
For j:=X[i-1]+1 to n-k+i do
*Ứng dụng: Tương tự như #3, bài này có thể giải quyết "trâu bò" rất nhiều bài toán khác.


VD: Với bài toán cái túi, khi số túi $(n =5)$ thì theo bài như trên
Nhiệm vụ cần là liệt kê tất cả các dãy con của dãy
$S={1, 2, 3, 4, 5}$ để xét các cách chọn các túi xem có thỏa mãn hay không.

Điều này khá dễ dàng dựa theo bài toán trên.


Giả sử ta liệt kê được một dãy con của dãy $S={1, 2, 3, 4, 5}$ với độ dài là $i $.
Chẳng hạn
i=3.
A[1]=1;
A[2]=3;
A[3]=4;
Khi đó ta sẽ xét xem trường hợp chọn các túi thứ $1, 3, 4$ có thỏa mãn điều kiện ràng buộc của đầu bài hay không, tiếp tục xét như vậy, ta sẽ vét cạn được tất cả khả năng chọn.

Theo mình, cách này rất trâu bò, chỉ dùng khi vào đường cùng
 
Last edited by a moderator:
E

englandhuynh

#2 :
Mã:
  j:=i+i;
Cái này nên để là [TEX] j = i * i [/TEX] vì [tex]p[2i]=fasle[/tex] khi [TEX] i=2[/TEX] ...[TEX] p[(i-1)i]=false[/TEX] khi [TEX]i=i-1[/TEX]
 
Last edited by a moderator:
T

thienvamai

là j=i+i đấy
Animation_Sieb_des_Eratosthenes.gif
 
E

englandhuynh

i = 2 -> p[2i] = false
Để p[i+i] lại xét đến p[2i] nữa rồi !
 
Last edited by a moderator:
T

thienvamai

Mã:
procedure sieve(n:integer);
 begin
      fillchar(p, sizeof(p), true);
      for i:=2 to trunc(sqrt(n)) do
           if (p[i]) then
 begin
   [B]   j:=i+i;[/B] [I]Chỗ này cơ[/I]
      while (j<=n) do
      begin
           p[j]:=false;
           j:=j+i;
      end; 
 end;
 end;
thuật toán chính xác của sàng là gán j = 2*i mà
góp ý tí
Mã:
procedure sieve(n:integer);
 begin
      fillchar(p, sizeof(p), true);
      for i:=2 to [COLOR="Red"]trunc(sqrt(n)) [/COLOR]do
           if (p[i]) then
 begin
      j:=i+i;
      while (j<=n) do
      begin
           p[j]:=false;
           j:=j+i;
      end;
 end;
 end;
những phép tính liên quan đến số thực thời gian thực hiện rất lâu(so với các phép chỉ liên quan đến số nguyên), hơn nữa có nhiều bài toán yêu cầu đếm số lượng số nguyên tố hay tương tự, nên cứ for đến n cũng không ảnh hưởng đến hoà bình thế giới,không cần dừng lại ở [TEX]\sqrt{n}[/TEX]
 
M

mikelhpdatke

thuật toán chính xác của sàng là gán j = 2*i mà
góp ý tí
Mã:
procedure sieve(n:integer);
 begin
      fillchar(p, sizeof(p), true);
      for i:=2 to [COLOR=Red]trunc(sqrt(n)) [/COLOR]do
           if (p[i]) then
 begin
      j:=i+i;
      while (j<=n) do
      begin
           p[j]:=false;
           j:=j+i;
      end;
 end;
 end;
những phép tính liên quan đến số thực thời gian thực hiện rất lâu(so với các phép chỉ liên quan đến số nguyên), hơn nữa có nhiều bài toán yêu cầu đếm số lượng số nguyên tố hay tương tự, nên cứ for đến n cũng không ảnh hưởng đến hoà bình thế giới,không cần dừng lại ở [TEX]\sqrt{n}[/TEX]
Trunc(sqrt(n)) cho ra số nguyên
Còn Int(sqrt(n)) cho số thực :D. Mà for đến n cũng không ảnh hưởng thật
 
N

nghgh97

ra số nguyên nhưng hình như máy tính vẫn tính ra số thực rồi mới đổi về nguyên
Không biết gì về vụ này, nhưng mình nghĩ là u nói đúng. Căn thức thì luôn luôn phải xác định trong tập số thực rồi, sau đó mới đổi về số nguyên chứ.
Cho mình hỏi cái: $\sqrt{2}$ đổi ra số nguyên là bao nhiêu nhỉ? @-)
 
T

thienvamai

Không biết gì về vụ này, nhưng mình nghĩ là u nói đúng. Căn thức thì luôn luôn phải xác định trong tập số thực rồi, sau đó mới đổi về số nguyên chứ.
Cho mình hỏi cái: $\sqrt{2}$ đổi ra số nguyên là bao nhiêu nhỉ? @-)
pascal thì không biết, còn trong C++ thì ra 1 :3
 
M

mikelhpdatke

Không biết gì về vụ này, nhưng mình nghĩ là u nói đúng. Căn thức thì luôn luôn phải xác định trong tập số thực rồi, sau đó mới đổi về số nguyên chứ.
Cho mình hỏi cái: $\sqrt{2}$ đổi ra số nguyên là bao nhiêu nhỉ? @-)
Lấy phần nguyên hay làm tròn thì cũng là 1 hết:p
 
E

englandhuynh

Không biết gì về vụ này, nhưng mình nghĩ là u nói đúng. Căn thức thì luôn luôn phải xác định trong tập số thực rồi, sau đó mới đổi về số nguyên chứ.
Cho mình hỏi cái: $\sqrt{2}$ đổi ra số nguyên là bao nhiêu nhỉ? @-)
Round hay trunc đều = 1 , đổi như toán ý
green11.gif

Thuật toán chính xác của sàng là gán j = 2*i mà
Trước giờ mình code toàn để j = i^2 thấy cũng không sai
 
Last edited by a moderator:
T

thienvamai

Tìm kiếm ưu tiên chiều rộng

Tìm kiếm ưu tiên chiều rộng (BFS)
Tìm kiếm ưu tiên chiều rộng , hay còn gọi là “loang”, là một trong những thuật toán duyệt đồ thị đơn giản nhất. Ý tưởng của nó được sử dụng trong nhiều thuật toán, chẳng hạn thuật toán Prim tìm cây khung nhỏ nhất, thuật toán Dijkstra tìm đường đi ngắn nhất, v.v...

Loang chủ yếu được sử dụng để tìm đường đi ngắn nhất theo số cạnh giữa hai đỉnh của một đồ thị. Ta hình dung từ một đỉnh nguồn s, ban đầu thuật toán loang khám phá các đỉnh đến được từ s, đó là lớp thứ nhất, sau đó lại khám phá các đỉnh chưa thăm và đến được từ lớp thứ nhất, đó là lớp thứ hai, v.v... Nghĩa là các đỉnh đến từ có khoảng cách k từ s luôn được khám phá trước các đỉnh có khoảng cách k+1 từ s.

Sau đây là mã giả của thuật toán loang: (thực ra là mã Pascal)

Mã:
For i:=1 to n do {n là số đỉnh}

            Trace[i]:=0;

Trace[s]:=-1; {s là đỉnh nguồn}

d[s]:=0; {d[i] là khoảng cách từ nguồn đến đỉnh i}

i:=1; j:=1; q[i]:=s; {q là hàng đợi}

While i<=j do

          Begin

              For k Î Adj[q[i]] do

                   If Trace[k]=0 then

                         Begin

                             Trace[k]:=q[i];

                                  D[k]:=D[q[i]]+1;

                              Inc(j);

                              q[j]:=k;
                       
                            End;

                 Inc(i);

End;

Về mặt trực quan, ta thấy thuật toán loang luôn tìm được đường đi ngắn nhất theo số cạnh giữa hai đỉnh của một đồ thị. Nhưng thực ra, cũng cần phải chứng minh điều này. Dưới đây là một số bổ đề, hướng chứng minh:

Ký hiệu S(s,v) là số cạnh ít nhất trên một đường đi nào đó giữa s và v, giá trị này còn được gọi là khoảng cách giữa s và v. Nếu không có đường đi thì d(s,v)= \infty . Một đường đi từ s đến v có số cạnh là S(s,v) được gọi là đường đi ngắn nhất (theo số cạnh) giữa s và v.

Bổ đề 1: Với mọi cạnh (u,v) thuộc G, ta có S(s,v)= S(s,u)+1

Bổ đề 2: Sau khi kết thúc thuật toán loang, với mọi đỉnh v giá trị d[v] trả về thỏa d[v] \geq S(s,v)

Chứng minh: có thể quy nạp theo số phép toán đẩy vào hàng đợi

Bổ đề 3: Giả sử trong qúa trình thực hiện thuật toán loang, hàng đợi Q chứa các đỉnh v1, v2, ... , vr, với v1 ở đầu hàng đợi và vr ở cuối. Thế thì d[vr] ≤ d[v1] + 1 và d[vi ] ≤ d[vi+1] với mọi i = 1, 2,..., r - 1.

Chứng minh: có thể quy nạp theo số phép toán hàng đợi

Hệ qủa 1: Giả sử đỉnh vi và vj được đưa vào hàng đợi trong qúa trình thực hiện thuật toán loang, và vi được đưa vào trước vj, thế thì d[vi] ≤ d[vj] ngay khi vj được đưa vào hàng đợi

Chứng minh: trực tiếp từ bổ đề 3 và tính chất mỗi đỉnh chỉ nhận giá trị d nhiều nhất một lần

Định lý: Sau khi kết thúc thuật toán loang, d[v]= S(s,v) với mọi đỉnh v thuộc G

Chứng minh:

Phản chứng. Gọi v là đỉnh có giá trị S(s,v) nhỏ nhất mà bị gán sai nhãn d[v], từ đó suy ra điều mâu thuẫn
 
  • Like
Reactions: Kyanhdo
T

thienvamai

Kiểu dữ liệu‎ > ‎Binary Indexed Tree - Cây chỉ số nhị phân

Binary Indexed Tree - Cây chỉ số nhị phân

Bài toán
(Giáo sư đang dạy học sinh về các con ruồi)
Có n cái hộp, được đánh số từ 1 đến n. Tại giây thứ s, các bạn học sinh có hai quyền lựa chọn :
- Yêu cầu 0 : giáo sư thả thêm j con ruồi vào hộp thứ i
- Yêu cầu 1 : giáo sư trả lời xem, từ hộp i đến hộp j, có bao nhiêu con ruồi (tại thời điểm đó)

Mục đích
Thực hiện hai thao tác sau:
- Tăng / gán a lên v đơn vi
- Cho biết tổng a[1] + ... + a bằng bao nhiêu

Độ phức tạp
thả ruồi : O(log n), tính tổng : O(log n).

Sample Input

5
0 1 4
0 2 3
0 5 1
1 1 3
0 4 1
1 1 5

(dòng đầu tiên là số hộp)
(định nghĩa dòng có dạng "0 i j" nghĩa là thả j con ruồi vào hộp i, "1 i j" nghĩa là trả lời xem từ i đến j có bao nhiêu con ruồi)

Sample Output

7
9

(7 và 9 là hai câu trả lời cho hai yêu cầu "1 1 3" và "1 1 5" của học sinh)

PHP:
#include <stdio.h>

class bit_t {
public:
    int n;       // must be assigned in runtime
    int T[2309]; // one-based
    int get(int i);
    void set(int i, int value);
};
 
int bit_t::get(int i){
    int s=0;
    while (i>0){
        s += T[i];
        i -= i & (-i);
    }
    return s;
}
 
void bit_t::set(int i, int value){
    while (i<=n){
        T[i]+=value;
        i += i & (-i);
    }
}
 
bit_t tr;
 
main(){
    int p, q, ch;
    scanf("%d", &tr.n);
for(;;){
    if (scanf("%d%d%d", &ch, &p, &q) != 3) return 0;
    if (ch==0) tr.set(p, q);                         // increasing a[p] q units
    if (ch==1) printf("%d\n", tr.get(q) - tr.get(p-1)); // show a[p]+...+a[q]
}    
}
 
H

hungprokuto32

làm giúp mình bài tập này nha mình yếu môn tin học lắm :)
cho văn bản có tên sapxep.inp có cấu trúc như sau:
dòng 1: ghi số nguyên dương (n<= 50)
dòng 2: ghi n số nguyên a1 đến an
hãy viết chương trình sắp xếp các số trên theo thứ tự tăng dần. kết quả ghi được ghi vào tệp sapxep.out gầm 1 dòng, mỗi số cách nhau 1 dấu cách
nhờ các bạn đó
 
K

kinkami

làm giúp mình bài tập này nha mình yếu môn tin học lắm :)
cho văn bản có tên sapxep.inp có cấu trúc như sau:
dòng 1: ghi số nguyên dương (n<= 50)
dòng 2: ghi n số nguyên a1 đến an
hãy viết chương trình sắp xếp các số trên theo thứ tự tăng dần. kết quả ghi được ghi vào tệp sapxep.out gầm 1 dòng, mỗi số cách nhau 1 dấu cách
nhờ các bạn đó
Nếu là pascal thì bạn dùng Bubble Sort nhé!
hoặc Quick Sort cũng được
 
N

ngoctruong9x

cho hỏi sàng nguyên tố ý. nếu bài cho liệt kê số nguyên tố <=2 tỉ thì mảng đánh dấu đó có trữ đc không?
 
Top Bottom