1. Trang chủ
  2. » Trung học cơ sở - phổ thông

CẤU TRÚC dữ LIỆU đặc BIỆT KHI KHAI THÁC STL TRONG c++

31 84 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 31
Dung lượng 67,53 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Hàng đợi ưu tiên và Hàng đợi hai đầu là hai cấu trúc dữ liệu trừu tượng rất hữu ích trong quá trình giải các bài toán trong các kì thi học sinh giỏi. Về nội dung kiến thức của hàng đợi hai đầu và hàng đợi ưu tiên đã có rất nhiều tài liệu đề cập đến, trong chuyên đề này tôi không trình bày lại khái niệm, nguyên lí hoạt động mà chỉ nhắc lại các thao tác cơ bản trên hàng đợi để đưa vào áp dụng để giải một số bài toán cụ thế, và cũng là tài liệu tham khảo cho học sinh và giáo viên trong quá trình học tập và giảng dạy các đội tuyển học sinh giỏi.

Trang 1

CẤU TRÚC DỮ LIỆU ĐẶC BIỆT KHI KHAI THÁC STL TRONG C++

A MỞ ĐẦU

I Lý do chọn đề tài

Trong chuyên đề hội thảo năm 2014 ở Vĩnh Phúc các thầy cô đã phân tích và trình bày rất đầy đủ nguyên lí hoạt động, cách cài đặt và ứng dụng của một số cấu trúc dữ liệu trừu tượng như Hàng đợi, Ngăn xếp, Heap-max… và ứng dụng của nó Tuy nhiên khi cài đặt bằng một ngôn ngữ lập trình cụ thể người viết phải xây dựng một số hàm và thủ tục để từ đó khai thác thuận lợi Ở ngôn ngữ lập trình C++ thì có sẵn các thư viện lưu trữ cấu trúc dữ liệu nâng cao thường gặp khi xử lí thuật toán Có rất nhiều loại cấu trúc dữ liệu đặc biệt, ta quan tâm đến 6 lại cấu trúc:

 Hàng đợi hai đầu : dequeue

 Ngăn xếp: stack

 Hàng đợi ưu tiên(đống) : priority-queue

 Tập hợp ánh xạ (set/map)

 Vector

 Danh sách liên kết (list)

Các thư viện có sẵn giúp chúng ta giảm chi phí về thời gian khi phải xây dựng cáchàm Đơn giản như trong Pascal các bạn phải viết thủ tục Quicksort (1,n) để sắp xếp mảnggồm n phần tử thì trong C++ chúng ta chỉ cần gõ dòng lệnh sort(a+1, a+n+1) là được ngaymảng a sắp xếp không giảm

Tuy nhiên trong chuyên đề này tôi xin trình bày hai cấu trúc dữ liệu:

 Hàng đợi hai đầu : dequeue

 Hàng đợi ưu tiên (đống): priority_queue

II.Mục đích của đề tài

Hàng đợi ưu tiên và Hàng đợi hai đầu là hai cấu trúc dữ liệu trừu tượng rất hữu íchtrong quá trình giải các bài toán trong các kì thi học sinh giỏi Về nội dung kiến thức củahàng đợi hai đầu và hàng đợi ưu tiên đã có rất nhiều tài liệu đề cập đến, trong chuyên đề nàytôi không trình bày lại khái niệm, nguyên lí hoạt động mà chỉ nhắc lại các thao tác cơ bản trênhàng đợi để đưa vào áp dụng để giải một số bài toán cụ thế, và cũng là tài liệu tham khảo chohọc sinh và giáo viên trong quá trình học tập và giảng dạy các đội tuyển học sinh giỏi

B NỘI DUNG:

I HÀNG ĐỢI HAI ĐẦU

1 Khai báo thư viện hàng đợi:

Để sử dụng hàng đợi hai đầu ta phải khai báo thư viện queue: #include <queue>

2 Định nghĩa

Hàng đợi hai đầu là một container cho phép thực hiện các thao tác sau:

+ Thêm một phần tử x vào cuối hàng đợi q: q.push_back(x)

+ Lấy ra một phần tử ở cuối hàng đợi q: q.pop_back()

+ Thêm một phần tử xvào đầu hàng đợi q: q.push_front(x);

+ Lấy ra một phần tử ở đầu hàng đợi q: q.pop_front();

Trang 2

Như vậy việc lấy ra và thêm vào diễn ra ở hai đầu hàng đợi

+ q.empty(): cho giá trị true nếu hàng đợi rỗng, giá trị false khi hàng đợi không rỗng.+ q.front(): cho giá trị của phần tử đứng ở đầu hàng đợi

+ q.back(): cho giá trị của phần tử đứng ở cuối hàng đơi

3 Một số ví dụ về khai báo hàng đợi

Ví dụ 1: Khai báo hàng đợi q có các phần tử kiểu int: deque <int> q;

Ví dụ 2: Khai báo hàng đợi q có các phần tử kiểu double: deque <double> q;

Ví dụ 3:Khai báo hàng đợi q có các phần tử kiểu long long:

deque <long long> q;

*Chú ý: Các phần tử trong hàng đợi đều phải cùng kiểu cũng có thể là một kiểu do người lập trình tự định nghĩa

Cho dãy số nguyên a1, a2, …,an và m truy vấn Truy vấn trên dãy con này là một lệnh

có dạng q(i,j) với ý nghĩa tìm phần tử nhỏ nhất trong dãy con ai, ai+1, …,aj (i≤j)

Cho m truy vấn Q(i1,j1), Q(i2, j2), …, Q(im,jm) thỏa mãn

1.i1≤i2≤….≤im

2 j1≤j2≤….≤jm

Hãy in ra các giá trị là câu trả lời cho các truy vấn tương ứng

Input: file dqueue.inp gồm có:

 Dòng đầu tiên ghi hai số nguyên dương n, m (1≤n,m≤105)

 Dòng thứ hai ghi n số nguyên a1, a2, …,an

 m dòng tiếp theo, dòng thứ k ghi hai số ik,jk thể hiện cho truy vấn thứ k (dữ liệu đảm bảo thỏa mãn điều kiện 1 và 2 ở trên)

Output : file dqueue.out gồm m dòng, dòng thứ k ghi kết quả của truy vấn thứ k

Thuật toán: Dễ dàng ta có thể tìm được kết quả của m truy vấn bằng cách sử dụng 2 vòng lặp for như sau:

for (int k=1; k<=m; k++)

Trang 3

{ res=oo;

for (int u=i[k]; u<=j[k]; u++) res=min(res, a[u]);

printf(“%d\n”,res);

}

Với tiếp cận cách trên thì độ phức tạp O (m2)

Cải tiến: Ta nhận thấy sau khi tìm được đoạn L=[i1,j1] ta xây dựng một danh sách các ứng cử viên cho chức “bé nhất” ql≤ql+1≤… ≤qr

+ Nếu lấy ra một phần tử có giá trị =ql khi đó giá trị bé nhất sẽ là ql+1

+ Khi thêm vào một phần tử x nếu x<qr khi đó qr sẽ không bao giờ là phần tử bé nhất khi

đó bỏ qr ra khỏi danh sách

Làm tương tự với qr-1, qr-1… và ta thấy khi đó trong danh sách chỉ còn phần tử <x khi

đó chỉ cần thêm x vào danh sách

+ Truy vấn 1 trên đoạn [1,3]: Đầu tiên đưa 2, 3 vào q Khi đưa 1 vào thì thấy do 1<3 nên đẩy 3 ra, 1<2 nên đẩy 2 ra khi đó đẩy 1 vào và bé nhất là 1in ra 1

+ Truy vấn 2 trên đoạn [2,3] : đẩy 3 vào và bé nhất là 1 in ra 1

+ Truy vấn 3 trên đoạn 3-4 đây 4 vào bé nhất là 1 in ra 1

+ Truy vấn 4 trên đoạn 3-5: đẩy 5 vào bé nhất là 1 in ra 1

+ Truy vấn 5: trên đoạn 4-5: đẩy 5 vào bé nhất là 4 in ra 4

Như vậy q hoạt động như hàng đợi 2 đầu do đó ta khai thác queue Ta sử dụng hàng đợi q

for (int u=1; u<=n; u++) scanf(“%d”,&a[u]);

for (int k=1; k<=m; k++) scanf(“%d%d\n”,&i[k],&j[k]);

printf(“%d\n”,q.front());

}

}

* Nhận xét: Mỗi phần tử vào hàng đợi một lần và ra khỏi hàng đợi không quá 1 lần Do

đó độ phức tạp của thuật toán không quá n

Trang 4

Bài tập 2: Đủ chất

Cũng như mọi sinh viên, Steve cố gắng đảm bảo ăn uống điều độ, đủ chất và tiết kiệm

Đã mấy năm rồi, sáng nào Steve cũng ăn hai cái bánh mỳ tròn và uống một cốc sữa đậu nành

Sữa đậu nành đóng hộp có thể giữ khá lâu, nhưng bánh mỳ thì không để dành được quá k

ngày Giá bánh mỳ thường xuyên biến động Nhờ tính tình vui vẻ cởi mở, Steve có quan hệ

rất tốt với người bán hàng và biết được giá bánh trong m ngày tính từ hôm nay Từ đó Steve

có thể lên kế hoạch để tiết kiệm nhất trong việc mua bánh mỳ

Ví dụ, bánh có thể giữ được trong hai ngày Giá bánh hôm này là 3 đồng/chiếc, giá ngày mai là 1 đồng/chiếc và giá ngày kia sẽ là 2 đồng/ chiếc Kế hoạch chi tiết kiệm của Steve se là: Hôm nay mua hai chiếc bánh mỳ tròn, ngày mai – sẽ mua 4 chiếc vừa ăn vừa để dành cho ngày kia Như vậy Steve phải chi tất cả là 3×2+2×4 = 10

Yêu cầu: Cho m, k và ci, i =1 ÷ m, trong đó c i – giá một chiếc bánh mỳ tròn bán

ngày thứ i (1 ≤ m, k, c i ≤ 105) Hãy xác định số tiền tối thiểu cần có và số lượng bánh phải mua ở mỗi ngày

Input: Vào từ file văn bản foot.inp:

Trang 5

memset(f,sizeof(f),0); // để lưu số tiền nhỏ nhất cần phải trả 2 chiếc bánh mì trong mỗi ngày

for (int u=1; u<=m; u++)

Bài 3 Sơn hàng rào

Tuấn Anh muốn sơn lại rào nhà mình Hàng rào của anh được ghép bởi N tấm ván liêntiếp, mỗi tấm rộng 1 cm và có chiều cao khác nhau Để sơn nhanh chóng và dễ dáng hơn,Tuấn Anh đã mua “Supper Pain Roller Deluxe” (một cây lăn xịn) Cây lăn có chiều rộng x

cm Mỗi lần dùng Tuấn Anh phải để cây lăn chạm vào bức tường hoàn toàn, nếu không sẽkhông sơn được gì cả Mặt khác, lúc nào cũng phải để cho cây lăn song song với mặt đất Nóicách khác, Tuấn Anh sẽ chọn ra x tấm ván liên tiếp và sơn từ dưới lên đến độ cao của tấm vánthấp nhất trong x tấm ván đó

Trang 6

Tuy nhiên, cây lăn không thể giúp Tuấn Anh sơn hết được hàng rào Phần diện tích cònlại không thể sơn bằng cây lăn, anh ta sẽ sơn bằng cọ nhỏ Hãy giúp Tuấn Anh tính xem phầndiện tích phải sơn bằng cọ nhỏ nhất có thể là bao nhiêu?

Trang 8

Bài tập 4: KẾ HOẠCH PHÓNG TÀU VŨ TRỤ

Cơ quan hàng không vũ trụ Mĩ NASA vừa thực hiện thành công một dự án lớn Đó làxây dựng một trạm vũ trụ trên mặt trăng Công việc trước mắt là duy trì trạm vũ trụ này trong

N ngày Để vận hành tốt, lúc nào cũng cần có một phi hành gia ở trên trạm vũ trụ Tuy nhiên,mỗi phi hành gia không thể ở trên trạm vũ trụ quá M ngày liên tiếp, vì vậy NASA cần lập một

kế hoạch luân phiên các nhà du hành vũ trụ Chi phí cho việc luân phiên này cũng khác nhauđối với mỗi ngày và NASA muốn tối thiểu tổng chi phí này Nhiệm vụ của bạn là đọc cácthông tin và đưa ra một kế hoạch tối ưu Chú ý rằng ở ngày thứ 1 luôn cần có sự thay đổi

Input: Vào từ file văn bản nasa.inp:

4(Thay người ở các ngày thứ 1,

3, 5, 8)

Trang 9

Phân tích bài toán:Gọi f[i] là tổng chi phí ít nhất cần có để duy trì sự sống trên trạm vũ trụ đến ngày thứ i Khi đó kết quả cần tìm là f[n].

Công thức quy hoạch động

Để tính được f[i] ta dựa vào mảng g[i]

if (i>m) for (int j=i-m; j<i; j++) f[i]=min(g[j],f[i]);

else for (int j=0; j<=i-1; j++) f[i]=min(g[j],f[i]);

Trang 10

if (i>m) if (q.front()==g[i-m]) q.pop_front();

Bài 5 Dãy dài bậc k

Cho dãy số nguyên a1, a2, ,an Một dãy con của dãy đã cho là dãy các phần tử liêntiếp nhau Dãy con ai, ai+1, …,aj được gọi là dãy con đều bậc k nếu như chênh lệch giữa hai sốbất kỳ trong dãy này không vượt quá k Hãy tìm dãy con đều bậc k có độ dài lớn nhất

Input: sequarek.inp

 Dòng đầu tiên ghi hai số nguyên dương n, k (1≤n≤106,1≤k≤109)

 Dòng thứ hai ghi n số nguyên a1, a2, …,an (│ai│≤ 109)

Output: một số nguyên duy nhất là độ dài của dãy con tìm được

}

Do 1≤n≤106 nên để cải tiến chương trình ta sử dụng hàng đợi sẽ giúp chương trìnhchạy nhanh với n lớn Để tiết kiếm chi phí thời gian ta dùng hai hàng đợi q và p trong đó hàngđợi q có đặc điểm phần tử đứng trước có giá trị lớn hơn hoặc bằng giá trị của phần tử đứng

Trang 11

sau Còn hàng đợi p có đặc điểm phần tử đứng trước có giá trị nhỏ hơn hoặc bằng giá trị củaphần tử đứng sau Để kiểm tra xem a[v]-a[u]>k ta sử dụng q.front-p.front>k

Trang 12

Bài tập 6 Ếch săn mồi

Có m bậc thang đánh số từ 1 đến m từ trên xuống dưới Mỗi bậc thang được chia đều thành n ô Ô thứ j của bậc thang i được gọi là ô (i,j) và trên đó có lượng thức ăn aij

Một con ếch muốn đi săn mồi trên những bậc thang Ếch được xuất phát từ một ô tùy ý trên bậc thang 1 và nhảy dần xuống bậc thang m Khi nhảy tới ô nào thì ếch sẽ ăn hết thức ăn trong ô đó Tuy nhiên có một hạn chế là từ ô (x,y) chú ếch chỉ được phép nhảy sang ô (x’,y’) nếu:

Yêu cầu : Tìm một cách đi kiếm ăn cho chú ếch sao cho tổng lượng thức ăn kiếm được

là lớn nhất

Input frog.inp gồm có:

- Dòng 1 chứa ba số nguyên dương m, n, k ≤1000

- M dòng tiếp theo, dòng thứ i chứa n số nguyên dương , số thứ j là aij≤109

Output frog.out gồm có:

- Dòng 1 ghi tổng lượng thức ăn kiếm được

- M dòng tiếp theo, dòng thứ i ghi một số nguyên là số hiệu ô đi qua trên bậcthang i

Đáp số cần tìm chính là max{f[m][j], j=1 n}

Để tính f[i][j] ta phải sử dụng ba vòng lặp for dẫn đến chương trình chạy chậm tức làkhông hiệu quả Để giảm chi phí thời gian ta sử dụng một hàng đợi hai đầu để tính f[i][j]

Hoạt động của hàng đợi q luôn đưa vào cuối hàng đợi phần tử có giá trị lớn hơn phần

tử trước đó Phần tử đầu hàng đợi được lấy để tính mảng f khi thỏa mãn không quá k

Trang 13

scanf("%d%d%d",&m,&n,&k);

for (int i=1; i<=m; i++)

for (int j=1; j<=n; j++) scanf("%d",&a[i][j]);

int u=0; int v=0;

while (!q.empty()) q.pop_back();

res=0; int id=1;

for (int i=1; i<=n; i++)

if (res<f[m][i])

{

res=f[m][i];

Trang 14

Bài tập 7: THỜI TRANG CỦA BÒ

Tại hội diễn thời trang mới nhất Mẫu thời trang đang thịnh hành của các con bò là cóhai vệt trắng trên da của nó Nông dân John đã mua một con bò như vậy Thật không may,thời trang luôn thay đổi nhanh chóng và bây giờ mẫu thời trang thịnh hành lại là chỉ có mộtvệt trắng trên da(‼!)

Nông dân John muốn tạo lại mẫu thời trang mới cho con bò của mình bằng cách trộn hai vệttrắng thành một Da của con bò có thể mô tả như là một lưới n hàng, m cột (1≤n,m≤50) gồmcác kí tự dạng:

Trang 15

Nông dân John dùng một cái bút vẽ nhỏ để thực hiện việc trộn hai vệt trắng thành một.

Cụ thể anh ta sẽ tô một số ô thành màu trắng để sau khi tô trên tấm da chỉ còn một vệt Trong

ví dụ trên anh ta cần tô 3 ô thành màu trắng (được đánh dấu bởi ‘*’ trong hình dưới đây):

 Dòng đầu tiên ghi hai số nguyên dương n, m

 Dòng 2 đến n+1 : Mỗi dòng chứa một xâu kí tự độ dài m chỉ gồm kí tự ‘X’ hoặc

‘.’ Mô tả hình dạng trên da con bò

Output : pageant.out: Một số nguyên duy nhất là số lượng ô ít nhất cần phải tô thànhmàu trắng

Example

6 16

Trang 16

void doc()

{

scanf("%d%d",&n,&m);

for (int i=1; i<=n; i++) {

for (int j=1; j<=m; j++) scanf("%c",&a[i][j]);

Trang 18

II HÀNG ĐỢI ƯU TIÊN (ĐỐNG)

1 Khái niệm

Hàng đợi ưu tiên là một kiểu dữ liệu trừu tượng tập hợp đặc biệt, trong đó mỗi phần tử

có một độ ưu tiên nào đó.Độ ưu tiên của phần tử thường là một số, theo đó, phần tử có độ ưutiên nhỏ nhất sẽ được ‘ưu tiên’ nhất Một cách tổng quát thì độ ưu tiên của một phần tử là mộtphần tử thuộc tập hợp được xếp theo thứ tự tuyến tính

Hàng đợi ưu là một loại container adaptor, được thiết kế đặc biệt để phần tử ở đỉnhluôn luôn là phần tử có độ ưu tiên lớn nhất so với các phần tử khác.Nó giống như một heap,

mà ở đây là heap max, tức là phần tử có độ ưu tiên lớn nhất có thể được lấy ra và các phần tửkhác được chèn vào bất kì.Lợi ích của cấu trúc hàng đợi ưu tiên luôn cho chúng ta sẵn cácthao tác chỉ việc khai thác mà không mất thời gian xây dựng

2 Khai báo thư viện:

Để khai thác và sử dụng hàng đợi ưu tiên ta phải khai báo thư viện queue:

#include <queue>

3 Các thao tác

Hàng đợi ưu tiên (đống) là một container cho phép thực hiện các thao tác sau:

+ Thêm một phần tử x vào hàng đợi ưu tiên h: h.push(x)

+ Lấy ra khỏi hàng đợi ưu tiên phần tử có giá trị lớn nhất : h.pop()

** Như vậy việc lấy ra và thêm vào diễn ra một đầu

+ h.empty(): cho giá trị true nếu hàng đợiưu tiên không có phần tử nào, giá trị false khi hàng đợi ưu tiên không rỗng

+ h.top(): cho giá trị lớn nhất của đống h

+ h.size(): cho biết số lượng phần tử trong đống h

4 Một số ví dụ về khai báo hàng đợi ưu tiên

Ví dụ 1: khai báo hàng đợi ưu tiên q có các phần tử kiểu int

priority_queue <int> q;

Ví dụ 2: Khai báo hàng đợi ưu tiên q có các phần tử kiểu double;

priority_ queue <double> q;

*Chú ý: Các phần tử trong hàng đợi đều phải cùng kiểu cũng có thể là một kiểu do

người lập trình tự định nghĩa

Ví dụ 1: Khai báo Hàng đợi ưu tiên q có các phần tử mà mỗi phần tử là một cặp dữ liệutypedef pair< int, int> II;

priority_queue <II> q;

Ví dụ 2: Hàng đợi ưu tiên q có các phần tử mà mỗi phần tử là một cặp ba số nguyên:

typedef pair<int, pair<int, int>> III;

priority_queue <III> q;

5 Bài tập áp dụng

Để hiểu và vận dụng khai thác được hàng đợi ưu tiên vào giải các bài toán tôi thường cho học sinh làm một số bài tập từ đơn giản nhất đến khó dần để các em học sinh dễ tiếp thu

Bài tập 1 Hàng đợi ưu tiên Max

Cho trước một danh sách rỗng Người ta xét hai thao tác trên danh sách đo:

Trang 19

- Thao tác “+V” (ở đây V là một số tự nhiên≤109): Nếu danh sách đang có ít hơn 15000phần tử thì thao tác này bổ sung thêm phần tử V vào danh sách , nếu không thao tácnày không có hiệu lực.

- Thao tác “- ”: Nếu danh sách đang không rỗng thì thao tác này loại bỏ tất cả các phần

tử lớn nhất của danh sách Nếu không thao tác này không có hiệu lực

Ví du: Với danh sách ban đầu rỗng:

- Nếu ta thực hiện liên tiếp các thao tác : +1, +3, +2, +3 ta sẽ được danh sách (1,3,2,3)

- Thực hiện thao tác -, ta sẽ được danh sách (1,2)

- Thực hiện hai thao tác +4 ta sẽ được danh sách (1,2 ,4, 4)

- Thực hiện thao tác – ta sẽ được danh sách (1,2)

- Tiếp tục với các thao tác +2, +9, +7, +8 ta sẽ được danh sách (1, 2, 2, 9, 7, 8)

- Cuối cùng ta thực hiện thao tác -, ta còn lại danh sách (1, 2, 2, 7, 8)

Vấn đề đặt ra là cho trước một dãy không quá 100000 thao tác.Hãy xác định những giá trị

số nào còn lại trong danh sách , mỗi giá trị chỉ được liệt kê một lần

Input io.inp gồm nhiều dòng, mỗi dòng ghi một thao tác Thứ tự các thao tác trên các

dòng liệt kê theo đúng thứ tự sẽ thực hiện

Output io.out gồm có 2 dòng:

Dòng 1: Ghi số lượng những giá trị còn lại trong danh sách

Dòng 2: Liệt kê những giá trị đó theo thứ tự giảm dần

Example

Io.inp Io.out+1

+3+2+3-+4+4-+2+9+7+8-

4

8 7 2 1

Hướng dẫn: Ta nhận thấy sau mỗi thao tác thêm vào thì danh sách mới luôn được sắp

xếp theo thứ tự giảm dần, còn khi loại bỏ phần tử trong danh sách thì phần tử bị loại bỏ luôn

có giá trị lớn nhất Với bài toán trên theo cách làm thông thường thì sau khi thêm một phần

tử vào danh sách ta lại phải sắp xếp lại danh sách đó theo thứ tự giảm dần Việc đó mất nhiềuthời gian vì ta phải duyệt lại cả danh sách Ta nhận thấy dựa vào tính chất của hàng đợi ưutiên việc đưa ra dãy số còn lại sau 100000 thao tác trở lên đơn giản nhiều Đây chính là lợithế của hàng đợi ưu tiên

Chương trình

Ngày đăng: 09/03/2021, 13:26

TỪ KHÓA LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm

w