Professional Documents
Culture Documents
vn/c-mang-danh-dau
#include <stdio.h>
int main(){
int n = 12, x = 24, y = 6;
int a[12] = {1, 2, 3, 4, 5, 5, 7, 9, 13, 24, 27, 28};
if(binary_search(a, n, x)){
printf("FOUND\n");
}
else printf("NOT FOUND\n");
if(binary_search(a, n, y)){
printf("FOUND\n");
}
else printf("NOT FOUND\n");
return 0;
}
Output :
FOUND
NOT FOUND
int main(){
int n = 10, x = 3;
int a[10] = {1, 1, 2, 2, 3, 3, 3, 5, 7, 9};
int res = firstPos(a, n, x);
if(res == -1){
printf("%d khong xuat hien trong mang\n");
}
else{
printf("Vi tri dau tien cua %d trong mang : %d\n", x, res);
}
return 0;
}
Output :
Vi tri dau tien cua 3 trong mang : 4
Bài toán 2 : Tìm vị trí cuối cùng của phần tử X trong mảng đã sắp tăng dần
Tương tự như thuật toán tìm kiếm nhị phân nhưng khi tìm thấy phần tử X trong mảng thì ta chỉ
lưu lại kết quả và cố gắng tìm kiếm thêm ở nửa bên phải.
#include <stdio.h>
int main(){
int n = 10, x = 3;
int a[10] = {1, 1, 2, 2, 3, 3, 3, 5, 7, 9};
int res = lastPos(a, n, x);
if(res == -1){
printf("%d khong xuat hien trong mang\n");
}
else{
printf("Vi tri cuoi cung cua %d trong mang : %d\n", x, res);
}
return 0;
}
Output :
Vi tri cuoi cung cua 3 trong mang : 6
Bài toán 3 : Tìm vị trí đầu tiên của phần tử lớn hơn hoặc bằng X trong mảng đã sắp tăng dần
Tương tự như thuật toán tìm kiếm nhị phân nhưng khi tìm thấy phần tử ở giữa lớn hơn hoặc bằng
phần tử X thì ta chỉ lưu lại kết quả và cố gắng tìm kiếm thêm ở nửa bên trái.
#include <stdio.h>
int main(){
int n = 10, x = 4;
int a[10] = {1, 1, 2, 2, 3, 3, 3, 5, 7, 9};
int res = firstPos(a, n, x);
if(res == -1){
printf("%d khong xuat hien trong mang\n");
}
else{
printf("Vi tri dau tien cua phan tu >= %d trong mang : %d\n", x, res);
}
return 0;
}
Output :
Vi tri dau tien cua phan tu >= 4 trong mang : 7
Câu 3. Khiêu vũ
Câu 3. Khiêu vũ (5,0 điểm)
Ở thành phố Anpha xinh đẹp mọi người sống rất bình yên và hạnh phúc. Cuối tuần họ
thường tổ chức các buổi tiệc khiêu vũ cho cư dân trong thành phố. Sắp tới họ sẽ tổ chức một
buổi tiệc khiêu vũ mang tên “Vũ điệu mùa xuân”. Điều đặc biệt trong buổi tiệc này là chỉ
những cặp đôi có chiều cao chênh lệch nhau đúng bằng số k cho trước thì mới được khiêu vũ
cùng nhau (không phân biệt giới tính). Có 1 người tham gia buổi tiệc khiêu vũ, chiều cao của
n người này lần lượt là h1, h2, ..., hn.
Yêu cầu: Hãy đếm xem có tất cả bao nhiêu cặp đôi có thể khiêu vũ cùng nhau.
Dữ liệu: Vào từ tệp văn bản KHIEUVU.INP gồm 2 dòng:
• Dòng đầu ghi 2 số nguyên n, k với n là số lượng người tham gia buổi tiệc, k là độ
chênh lệch chiều cao yêu cầu (2 ≤ n ≤ 105
; 0 ≤ k ≤ 109
).
• Dòng thứ 2 ghi n số h1, h2, ..., hn là chiều cao của n người tham gia buổi tiệc (hi ≤ 109
).
Ràng buộc:
• 50% số test ứng với 50% số điểm thỏa mãn n ≤ 103, hi ≤ 106
• 50% số test còn lại ứng với 50% số điểm thỏa mãn n ≤ 105, hi ≤109
sort(h)
count = 0
for i in range(n):
j = bisect_left(h, h[i]+k, i+1, n)
if j < n and h[j] == h[i] + k:
count += 1
return count
sort(a+1,a+n+1);
int c = 0;
for(int i =1; i<= n;i++)
{ j = tknp(h,n,h[i] + k);
If( j<=n) and (a[j] == a[i] + k)
c++;
}
Bài tập.
Cho số nguyên dương n, tiếp theo là n số nguyên dương của một dãy a, cuối cùng là một
số s.
Hãy đưa ra dãy con liên tiếp đầu tiên của dãy a sao cho tổng của dãy đó bằng s. In dãy đó ra
màn hình, sau mỗi phần tử có một khoảng trắng. Nếu không tồn tại dãy đó thì in ra "-1".
Ví dụ:
Test mẫu 1:
Input Output
5
12345 23
5
Với a = [1, 2, 3, 4, 5] và s = 5 thì kết quả mong muốn là: "2 3 ".
Test mẫu 2:
Input Output
3
123 -1
4
Với a = [1, 2, 3] và s = 4 thì kết quả mong muốn là: "-1"
Hướng dẫn bài tập.
Tạo dãy b, với b[0] = a[0], b[i] = b[i-1] + a[i].
Ví dụ với a = [1, 2, 3, 4, 5] và s = 5. Dãy b được tạo ra là [1, 3, 6, 10, 15].
Mục đích chúng ta cần làm là cần tìm vị trí l và i sao cho b[i] - b[l] = s.
Như vậy dãy cần tìm sẽ là các phần tử từ ví trí l+1 đến i ở trong dãy a.
Dùng biến i duyệt dãy b, nếu b[i] ≥ s (dãy đó có thể kết thúc tại i) tìm vị trí của b[i]-s trong
dãy b, nếu tồn tại vị trí l đó thì đưa ra dãy từ l+1 đến i.
Code mẫu:
Ngôn ngữ C++:
#include<iostream>
int a[100001];
int b[100001];
int BinSearch(int a[], int n, int x){
int l = 0, r = n-1;
while (l < r){
int mid = (l+r)/2;
if (a[mid] < x){
l = mid+1;
}
else{
r = mid;
}
}
if (a[l] == x){
return l;
}
return -1;
}
int main(){
int n, s;
cin >> n;
for (int i = 0; i < n; i++){
cin >> a[i];
}
cin >> s;
bool k = solve(a, b, n, s);
if (!k){
cout << -1;
}
return 0;
}
NỘI DUNG :
Dãy Con Cỡ K Có Tổng Lớn Nhất
Dãy Con Giống Nhau Dài Nhất
Dãy Con Khác Nhau Dài Nhất
Kiểm Tra Mảng Tăng Dần
Dãy Nguyên Tố Dài Nhất
Video Tutorial
Ví dụ A[] = {3, 2, 1, 5, 6, 7, 2} và K = 3 các bạn sẽ có các dãy con : {3, 2,1}, {2, 1, 5}, {1, 5, 6}, {5,
6, 7}, {6, 7, 2}
Thuật toán :
1. Duyệt các chỉ số i từ 0 tới N - K đại diện cho phần tử đầu tiên của dãy con
2. Tính tổng K phần tử liên tiếp từ chỉ số i và in ra màn hình
Code :
#include <stdio.h>
int main(){
int n = 7, k = 3;
int a[7] = {3, 2, 1, 5, 6, 7, 2};
int tong = 0;
for(int i = 0; i <= n - k; i++){
//reset giá trị của tổng ở mỗi dãy con
tong = 0;
for(int j = 0; j < k; j++){
tong += a[i + j];
}
printf("%d ", tong);
}
return 0;
}
Output :
6 8 12 18 15
Ví dụ A[] = {1, 2, 2, 2, 3, 3, 5, 4, 4, 4, 4, 2} thì dãy con {4, 4, 4, 4} là dãy con dài nhất chứa các
phần tử giống nhau
Thuật toán :
Code :
#include <stdio.h>
int main(){
int n = 12;
int a[12] = {1, 2, 2, 2, 3, 3, 5, 4, 4, 4, 4, 2};
int dem = 1, res = 1;
for(int i = 1; i < n; i++){
if(a[i] == a[i - 1]){
++dem;
}
else{
if(res < dem){
res = dem;
}
dem = 1;
}
}
//Chú ý cập nhật dãy con dài nhất kết thúc tại A[n - 1]
if(dem > res){
res = dem;
}
printf("%d\n", res);
return 0;
}
Output :
4
Bạn thử làm thêm yêu cầu liệt kê tất cả các dãy con giống nhau dài nhất, ví dụ A[] = {1, 2, 2, 2, 3,
3, 5, 4, 4, 4, 3, 2} thì có 2 dãy con thỏa mãn là {2, 2, 2} và {4, 4, 4}
Ví dụ A[] = {1, 2, 3, 2, 3, 3, 5, 4, 4, 4, 4, 2} thì dãy con {1, 2, 3, 2, 3} là dãy con cần tìm
Thuật toán :
#include <stdio.h>
int main(){
int n = 12;
int a[12] = {1, 2, 3, 2, 3, 3, 5, 4, 4, 4, 4, 2};
int dem = 1, res = 1;
for(int i = 1; i < n; i++){
if(a[i] != a[i - 1]){
++dem;
}
else{
if(res < dem){
res = dem;
}
dem = 1;
}
}
//Chú ý cập nhật dãy con dài nhất kết thúc tại A[n - 1]
if(dem > res){
res = dem;
}
printf("%d\n", res);
return 0;
}
Output :
Thuật toán :
Xét N - 1 cặp phần tử đứng cạnh nhau, nếu N - 1 cặp phần tử này đều thỏa mãn phần tử đứng
sau A[i] lớn hơn hoặc bằng phần tử ngay trước nó là A[i - 1] thì mảng tăng dần
Chú ý không cần kiểm tra A[0] và phần tử đứng trước nó.
Code :
#include <stdio.h>
int main(){
int n = 6;
int a[6] = {1, 2, 2, 3, 4, 5};
if(check(a, n)){
printf("Mang tang dan\n");
}
else{
printf("Mang khong tang dan\n");
}
return 0;
}
Output :
Mang tang dan
Ví dụ A[] = {2, 3, 5, 8, 7, 11, 19, 4, 10, 23, 5} thì dãy con {7, 11, 19} có độ dài 3 và có tổng lớn
nhất.
Thuật toán :
Code :
#include <stdio.h>
#include <math.h>
int main(){
int n = 11;
int a[11] = {2, 3, 5, 8, 7, 11, 19, 4, 10, 23, 5};
int dem = 0, tong = 0;
int res_dem = 0, res_tong = 0, start = -1;
for(int i = 0; i < n; i++){
if(prime(a[i])){
++dem;
tong += a[i];
}
else{
if(dem > res_dem){
res_dem = dem;
res_tong = tong;
start = i - res_dem;
}
else if(dem == res_dem){
if(tong > res_tong){
res_tong = tong;
start = i - res_dem;
}
}
dem = 0; tong = 0;
}
}
//cap nhat day con ket thuc tai a[n - 1]
if(dem > res_dem){
res_dem = dem;
res_tong = tong;
start = n - res_dem;
}
else if(dem == res_dem){
if(tong > res_tong){
res_tong = tong;
start = n - res_dem;
}
}
if(start == -1){
printf("Mang khong chua so nguyen to !\n");
}
else{
for(int i = 0; i < res_dem; i++){
printf("%d ", a[i + start]);
}
}
return 0;
}
Output :
7 11 19
1. Lý Thuyết Mảng Đánh Dấu
Bài Toán : Cho mảng A[] gồm N phần tử là các số nguyên trong đoạn [0, 106]. Hãy đếm các giá trị khác
nhau trong mảng ?
Mình đã hướng dẫn các bạn cách sử dụng 2 vòng for lồng nhau để giải quyết bài toán trên, nếu bạn
chưa biết cách làm đó có thể tham khảo tại đây
Để giải quyết bài toán trên bạn có thể dùng 1 mảng đánh dấu đủ lớn để đánh dấu sự xuất các giá trị
trong mảng, vì các phần tử trong mảng đều nằm trong đoạn từ [0, 106] nên chỉ cần dùng một mảng số
nguyên mà chỉ số của nó có thể đánh dấu được hết các giá trị [0, 106] là giải quyết được bài toán.
Ý tưởng ở đây đó là bạn sẽ sử dụng chỉ số của mảng đánh dấu để đánh dấu giá trị tương ứng với chỉ số
đó đã xuất hiện trong mảng. Ban đầu tất cả các phần tử trong mảng đánh dấu có giá trị là 0, khi bạn gặp
một giá trị trong mảng thì sẽ chuyển phần tử trong mảng đánh dấu tại chỉ số đó thành 1.
Ví dụ mảng đánh dấu là mark[] và khi gặp phần tử trong mảng A[] là 3 thì mark[3] = 1
Sau khi đánh dấu xong các giá trị xuất hiện thì bạn chỉ cần duyệt mảng đánh dấu và xem phần tử nào
trong mảng đánh dấu có giá trị là 1 để đếm hoặc liệt kê.
Code :
#include <stdio.h>
//Chỉ số của mark là từ 0 tới 10^6, ban đầu toàn bộ mảng mark[] = 0
int mark[1000001];
int main(){
int n = 8;
int a[8] = {3, 1, 3, 0, 2, 4, 1, 6};
//đánh dấu
for(int i = 0; i < n; i++){
//Lấy a[i] làm chỉ số và chuyển mark[a[i]] = 11
mark[a[i]] = 1;
}
int dem = 0;
//Bạn có thể duyệt từ i = 0 tới giá trị max trong mảng cũng được
for(int i = 0; i <= 1000000; i++){
if(mark[i] == 1){
++dem;
}
}
printf("So luong phan tu khac nhau : %d\n", dem);
return 0;
}
Output :
i 0 1 2 3 4 5 6 7 ...
mark[i] 0 0 0 0 0 0 0 0 0.
i = 0, a[i] = 3, mark[3] = 1
i 0 1 2 3 4 5 6 7 ...
mark[i] 0 0 0 1 0 0 0 0 0.
i = 1, a[i] = 1, mark[1] = 1
i 0 1 2 3 4 5 6 7 ...
mark[i] 0 1 0 1 0 0 0 0 0.
i = 2, a[i] = 3, mark[3] = 1
i 0 1 2 3 4 5 6 7 ...
mark[i] 0 1 0 1 0 0 0 0 0.
....
i = 7, a[i] = 6, mark[6] = 1
i 0 1 2 3 4 5 6 7 ...
mark[i] 1 1 1 1 1 0 1 0 0.
Mảng đánh dấu không thể sử dụng được với số âm, vì chỉ số trong mảng không thể là số âm
Mảng đánh dấu không sử dụng được khi giá trị cần đánh dấu vượt chỉ số của mảng mà bạn có
thể khai báo
Thông thường bạn chỉ áp dụng được kỹ thuật này khi các phần tử trong mảng có giá trị trong
đoạn [0, 107]
Chú ý sử dụng mảng đánh dấu đủ lớn để không lỗi bộ nhớ, ví dụ bạn cần đánh dấu các phần tử
trong đoạn [0, 103] thì mảng cỡ 1001 là đủ rồi, hoặc có thể lớn hơn
Bài Toán 1 : Cho mảng A[] gồm N phần tử là số nguyên trong đoạn [0, 106], hãy liệt kê các giá trị khác
nhau trong mảng theo thứ tự từ nhỏ tới lớn
Ý tưởng là sử dụng mảng đánh dấu sau đó liệt kê các số từ nhỏ tới lớn xuất hiện trong mảng và in ra
nếu giá trị đó được đánh dấu.
Code :
#include <stdio.h>
//Chỉ số của mark là từ 0 tới 10^6, ban đầu toàn bộ mảng mark[] = 0
int mark[1000001];
int main(){
int n = 10;
int a[10] = {3, 1, 3, 0, 2, 4, 1, 14, 12, 7};
//đánh dấu
int max_val = -1000000000;
for(int i = 0; i < n; i++){
//Lấy a[i] làm chỉ số và chuyển mark[a[i]] = 11
mark[a[i]] = 1;
if(a[i] > max_val){
max_val = a[i];
}
}
printf("Gia tri khac nhau trong mang : ");
for(int i = 0; i <= max_val; i++){
if(mark[i] == 1){
printf("%d ", i);
}
}
return 0;
}
Output :
Bài Toán 2 : Cho mảng A[] gồm N phần tử là số nguyên trong đoạn [0, 106], hãy liệt kê các giá trị khác
nhau trong mảng theo thứ tự xuất hiện trong mảng, mỗi giá trị chỉ liệt kê 1 lần
Ý tưởng là sử dụng mảng đánh dấu sau đó liệt kê các theo thứ tự xuất hiện trong mảng và in ra nếu giá
trị đó được đánh dấu.
Sau khi in ra giá trị thì bạn bỏ đánh dấu giá trị đó để tránh in trùng.
Code :
#include <stdio.h>
//Chỉ số của mark là từ 0 tới 10^6, ban đầu toàn bộ mảng mark[] = 0
int mark[1000001];
int main(){
int n = 10;
int a[10] = {3, 1, 3, 0, 2, 4, 1, 14, 12, 7};
//đánh dấu
for(int i = 0; i < n; i++){
//Lấy a[i] làm chỉ số và chuyển mark[a[i]] = 11
mark[a[i]] = 1;
}
printf("Gia tri khac nhau trong mang : ");
for(int i = 0; i < n; i++){
if(mark[a[i]] == 1){
printf("%d ", a[i]);
//Bỏ đánh dấu
mark[a[i]] = 0;
}
}
return 0;
}
Output :
Mảng đánh dấu rất hữu dụng đối với những bài toán liên quan tới tần suất xuất hiện của phần tử trong
mảng.
Trong bài toán liên quan tới tần suất thì mảng đánh dấu mark[] có vai trò đếm tần suất thay vì chỉ đánh
dấu. Khi đánh dấu giá trị a[i] trong mảng thì bạn cần cho mark[a[i]] = 1 còn khi bạn muốn đếm xem
a[i] đã xuất hiện bao nhiêu lần thì bạn thay đổi câu lệnh cập nhật thành mark[a[i]]++
Bài toán : Cho mảng A[] gồm N phần tử là số nguyên trong đoạn [0, 106], hãy đếm xem mỗi phần tử
trong mảng xuất hiện bao nhiêu lần và in ra theo thứ tự từ nhỏ tới lớn.
Code :
#include <stdio.h>
//Chỉ số của mark là từ 0 tới 10^6, ban đầu toàn bộ mảng mark[] = 0
int mark[1000001];
int main(){
int n = 10;
int a[10] = {3, 1, 3, 0, 2, 4, 1, 14, 3, 2};
//đếm tần suất
int max_val = -1000000000;
for(int i = 0; i < n; i++){
//Lấy a[i] làm chỉ số và chuyển mark[a[i]] tăng thêm 1 đơn vị
mark[a[i]]++;
if(a[i] > max_val){
max_val = a[i];
}
}
for(int i = 0; i <= max_val; i++){
if(mark[i]){
printf("%d xuat hien %d lan\n", i, mark[i]);
}
}
return 0;
}
Output :
Bài 1. Liệt kê các phần tử xuất hiện ít nhất 2 lần trong mảng theo thứ tự xuất hiện trong mảng, mỗi
phần tử chỉ liệt kê 1 lần
Bài 2. Liệt kê các phần tử xuất hiện đúng 1 lần trong mảng theo thứ tự xuất hiện trong mảng
Bài 3. Liệt kê các phần tử xuất hiện nhiều nhất trong mảng, nếu có nhiều phần tử có cùng số lần xuất
hiện nhiều nhất thì liệt kê theo thứ tự xuất hiện trong mảng
Bài 4. Cho mảng A[] và B[], hãy liệt kê các giá trị thuộc 1 trong 2 mảng, mỗi giá trị liệt kê 1 lần theo
thứ tự tăng dần