sáng tạo trong lập trình - Nhưng sáng tạo hay dành cho dân lập trình hay
Trang 1Thi HSG Quốc gia 2010
CCKLK: Dãy con chung không liền kề dài nhất
Cho dãy số nguyên dương x = (x 1 , x 2 , , x n ) Dãy y = (x i1 , x i2 , , x ik ) được gọi là dãy con không liền kề của dãy x nếu 1 i1 < i21 < < ik1 n Cho 2 dãy số nguyên a gồm n phần tử và b gồm m phần tử Xác định chiều dài k của dãy con chung không liền kề dài nhất của a và b 2 m, n 1000, 1 ai, bi 10000
CCKLK.INP CCKLK.OUT Giải thích
Gọi d(i,j) là đáp số của bài toán khi xét hai dãy a[1 i] và b[1 j] Ta có:
Nếu i ≤ 0 thì ta quy ước d(i,j) = 0,
Nếu a[i] = b[j] thì d(i,j) = d(i−2,j−2),
Nếu a[i] ≠ b[j] thì d(i,j) = max { d(i−1,j), d(i,j−1) }
Để cài đặt ta dùng 3 mảng một chiều x, y và z với ý nghĩa x[j] = d(i−2,j), y[j] = d(i−1,j) và z[j] = d(i,j) Khi
đó hệ thức trên được viết là:
Nếu i ≤ 0 thì ta quy ước d(i,j) = 0,
Nếu a[i] = b[j] thì d(i,j) = d(i−2,j−2) ứng với z[j] = x[j−2],
Nếu a[i] ≠ b[j] thì d(i,j) = max { d(i−1,j), d(i,j−1) } ứng với z[j] = max { y[j], z[j−1] }
Muốn tránh các phép copy dữ liệu từ y sang x; từ z sang y và từ x sang z ta chỉ cần tráo đổi các con trỏ mảng
Độ phức tạp
O(n.m)
Chương trình Pascal
(* CCKLK.PAS
k: chieu dai day con chung khong lien ke dai nhat
cua hai day so nguyen duong a[1 n], b[1 m]
*)
const fn = 'ccklk.inp'; gn = 'ccklk.out';
bl = #32; nl = #13#10; mn = 1001;
type int = integer;
mi1 = array[0 mn] of int;
var
n, m: int;
a, b: mi1;
function Max(a,b: int): int;
begin if a >= b then Max := a else Max := b; end;
Trang 3const char * fn = "ccklk.inp";
const char * gn = "ccklk.out";
for (i = 1; i <= n; ++i) f >> a[i];
for (i = 1; i <= m; ++i) f >> b[i];
cout << endl << "a: ";
for (i = 1; i <= n; ++i) cout << a[i] << " "; cout << endl << "b: ";
for (i = 1; i <= m; ++i) cout << b[i] << " "; f.close();
Trang 42 ONDINH.INP: Dòng đầu: n m s Từ dòng thứ hai trở đi:m cung
dạng u v Có thể có các cung trùng nhau (dư thừa)
Trang 5int len[mn]; // len[i] chieu dai s => i
char mark[mn]; // mark[i] danh dau dinh i:
// Chua xet 0; Co trong hang doi q 1; Da xu li 2
int d[mn]; // d[i] so luong duong ngan nhat s => i
int q[mn]; // hang doi
int n, m, s ; // so dinh n, so cung m, dinh xuat phat s // P R O T O T Y P E S
int main();
void Doc();
int XuLi();
int Min(int, int);
int BinSearch(cung [], int, int, int);
int Sanh(int,int,int,int);
void Ghi();
int main(){
Doc(); XuLi(); Ghi();
cout << endl << " Fini"; cin.get();
Trang 6j = c[k].b; // xet cac dinh j ke dinh i
int Sanh(int u1, int v1, int u2, int v2) {
if (u1 < u2) return -1;
if (u1 > u2) return 1;
Trang 7int Min(int, int);
int BinSearch(cung [], int, int, int);
int Sanh(int,int,int,int);
void Ghi();
int main(){
Doc(); XuLi(); Ghi();
cout << endl << " Fini"; cin.get();
Trang 8int Sanh(int u1, int v1, int u2, int v2) {
if (u1 < u2) return -1;
if (u1 > u2) return 1;
Trang 9m = k;
}
Mã số thuế
Xét tập S gồm tất cả các số 1 n trong hệ 36, 36 n 10 16 Cho số m: 3 m 70 Xét dãy số nguyên 1 < c 1
< c 2 < < c k < 36, k = (m1)/2 , x là số nguyên lớn nhất không vượt quá x
Chọn các số chứa các chữ số < c1 cấp cho 2 nhóm 1 và 2 rồi xóa các số này Chọn các số chứa các chữ số
< c2 cấp cho 2 nhóm 3, 4 Các số còn lại cấp cho 1 hoặc 2 nhóm cuối
1 n, a i 100000
Dữ liệu vào: Tệp văn bản diff.inp
Dòng đầu tiên: số n
Từ dòng thứ hai trở đi: dãy số a
Dữ liệu ra: Tệp văn bản diff.out chứa 2 số:
imax chỉ số đầu tiên của đoạn dài nhất tìm được trong dãy a
dmax số phần tử của doạn dài nhất
Các số trên cùng dòng cách nhau qua dấu cách
diff.inp diff.out Giải thích
Lần lượt đọc các phần tử ai của dãy a và đánh dấu vị trí xuất hiện của ai trong dãy thông qua mảng p, p[ai] =
i Với thí dụ đã cho, sau khi đọc và xử lý xong dãy a ta phải thu được
Trang 10p[2] = 2/7 cho biết số 2 lúc đầu xuất hiện tại vị trí 2 trong dãy a, sau đó xuất hiện tại vị trí 7 trong dãy a p[8] = p[10] = 0 cho biết các số 8 và 10 không xuất hiện trong dãy a
Ta gọi p là dãy trỏ ngược hay dãy vị trí của dãy a Ta xử lý từng đoạn d của ai như sau Mỗi đoạn d sẽ bao gồm một dãy liên tiếp các phần tử đôi một khác nhau tính từ chỉ số i đến j Thí dụ trên cho ta lần lượt 3 đoạn sau:
Đoạn thứ nhất d = a[1 4] = (5, 2, 4, 1), i = 1, j = 4,
Đoạn thứ hai d = a[2 6] = (2, 4, 1, 5, 3), i = 2, j = 6,
Đoạn thứ ba d = a[3 10] = (4, 1, 5, 3, 2, 7, 6, 9), i = 3, j = 10
Mỗi đoạn d được xác định như sau: Mỗi khi gặp phần tử aj đầu tiên trùng với một phần tử trong dãy tính từ
i thì ta cắt ra được đoạn d = a[i j1]
Với mỗi đoạn d[i j] ta tính số phần tử của đoạn đó là ji+1 và cập nhật giá trị dmax Để khởi trị cho đoạn tiếp theo, ta đặt i = p[aj]+1 Chú ý rằng p[aj] là vị trí xuất hiện của giá trị lặp aj
Độ phức tạp
O(n)
Chương trình Pascal
(* diff.pas
Tim doan dai nhat gom cac
phan tu doi mot khac nhau
imax, dmax: longint;
{ imax - chi so dau tien cua doan dai nhat
dmax - so phan tu cua doan dai nhat }
Trang 11Tim doan dai nhat gom cac
phan tu doi mot khac nhau
const char * fn = "diff.inp";
const char * gn = "diff.out";
const int mn = 100001;
int p[mn]; // p[v] = i: noi xuat hien so v trong day int n;
int imax; // chi so dau tien cua doan dai nhat
int dmax; // so phan tu cua doan dai nhat
Trang 12 Từ dòng thứ hai trở đi: dãy số a
Dữ liệu ra: Tệp văn bản hv1k.out chứa 2 số:
imax chỉ số đầu tiên của đoạn dài nhất tìm được trong dãy a
dmax số phần tử của đoạn dài nhất
Các số trên cùng dòng cách nhau qua dấu cách
hv1k.inp hv1k.out Giải thích
4 1 5 3 2 6
Thuật toán
Kí hiệu a[i j] là đoạn gồm các phần tử liên tiếp từ ai đến aj của dãy a và kí hiệu set(x) là tập chứa các phần
tử (khác nhau) của dãy x Với thí dụ đã cho ta có, a[1 5] = (5, 2, 4, 1, 5) và do đó set(a[1 5]) = {1, 2, 4, 5} Gọi p là dãy vị trí (trỏ ngược) của dãy a Vì dãy a gồm các phần tử đôi một khác nhau nên p cũng chứa các chỉ số đôi một khác nhau Khi đó a chứa một đoạn là hoán vị của k số tự nhiên đầu tiên (1,2, ,k) khi và chỉ khi tìm được hai chỉ số s và e thỏa đồng thời hai tính chất sau:
es+1 = k, và
set(a[s e]) = {1, 2, , k}
Trang 13Trong thí dụ trên ta tìm được s = 3, e = 8, k = 6, a[3 8] = (4, 1, 5, 3, 2, 6), và do đó set(a[3 8]) = {1, 2, 3, 4,
5, 6}
Xét dãy chỉ số 1, 2, , i thỏa tính chất j: 1 j i: p[j] ≠ 0
Để ý rằng điều kiện p[j] ≠ 0 tương đương với điều kiện giá trị j của dãy a xuất hiện tại vị trí p[j] Đặt s = min p[1 i] = min {p[1], p[2], , p[i]} và e = max p[1 i] = max {p[1], p[2], , p[i]} Ta thấy a chứa một đoạn là hoán vị của dãy (1,2, ,i) khi và chỉ khi es+1 = i
Tim doan dai nhat trong day so
doi mot khac nhau tao thanh mot
Trang 14Tim doan dai nhat trong day so
doi mot khac nhau tao thanh mot
const char * fn = "hv1k.inp";
const char * gn = "hv1k.out";
int Min(int, int);
int Max(int, int);
void Hv();
void Ghi();
Trang 15int Min(int a, int b) { return (a <= b) ? a : b; }
int Max(int a, int b) { return (a >= b) ? a : b; }
Trang 16Dữ liệu vào: Tệp văn bản hvsk.inp
Dòng đầu tiên: số n
Từ dòng thứ hai trở đi: dãy số a
Dữ liệu ra: Tệp văn bản hvsk.out chứa 2 số:
imax chỉ số đầu tiên của đoạn dài nhất tìm được trong dãy a
dmax số phần tử của đoạn dài nhất
Các số trên cùng dòng cách nhau qua dấu cách
hvsk.inp hvsk.out Giải thích
Khi duyệt p[vmin vmax] ta xét 2 trạng thái 0 và 1 như sau:
Trạng thái 0: Duyệt đoạn p toàn 0, p[2 3] và p[9 11]
Nếu gặp p[i] > 0 thì khởi trị cho trạng thái duyệt đoạn khác 0, p[istart ]:
Ghi nhận chỉ số đầu đoạn khác 0: istart = i,
Khởi trị cac giá trị pmin = min p[istart k1] và pmax = max p[istart k1] cho đoạn này: pmin =
pmax = p[i],
Chuyển qua trạng thái 1
Trạng thái 1: Duyệt đoạn p khác 0, p[1 1], p[4 8] và p[12 12]
Nếu p[i] > 0 thì cập nhật các chỉ số pmin và pmax Kiểm tra đẳng thức pmaxpmin = iistart để cập nhật chỉ số đầu tiên của đoạn hoán vị trong dãy a, imax và chiều dài của đoạn hoán vị, dmax
Nếu p[i] = 0 thì kết thúc việc duyệt đoạn p[isstart i1] này, chuyển qua trạng thái 0
Độ phức tạp
O(n)
Trang 17Chương trình Pascal
Chương trình CPP
/* - hvsk.cpp
Tim doan dai nhat trong day so
doi mot khac nhau tao thanh mot
hoan vi cua day so tu nhien lien tiep
const char * fn = "hvsk.inp";
const char * gn = "hvsk.out";
const int mn = 100002;
int p[mn];
int pmin, pmax;
int vmin, vmax;
int Min(int, int);
int Max(int, int);
void Hv(); // Sliding window
Trang 18}
f.close();
}
int Min(int a, int b) { return (a <= b) ? a : b; }
int Max(int a, int b) { return (a >= b) ? a : b; }
case 0: // duyet doan toan 0
if (p[i] > 0) { // Khoi tri khi gap so khac 0
pmin = pmax = p[i];
số tự nhiên 1 k; 1 n, a i 100000
Dữ liệu vào: Tệp văn bản hv1kmax.inp
Dòng đầu tiên: số n
Trang 19 Từ dòng thứ hai trở đi: dãy số a
Dữ liệu ra: Tệp văn bản hv1kmax.out chứa 2 số:
imax chỉ số đầu tiên của đoạn dài nhất tìm được trong dãy a
dmax số phần tử của đoạn dài nhất
Các số trên cùng dòng cách nhau qua dấu cách
hv1kmax.inp hv1kmax.out Giải thích
(* hv1kmax.pas: Tim doan dai nhat gom cac
phan tu tao thanh mot hoan vi cua 1 k *)
const fn = 'hv1kmax.inp'; gn = 'hv1kmax.out';
mn = 100002; bl = #32; nl = #13#10;
var p: array[0 mn] of longint;
{ p[v] - noi xuat hien gia tri v trong day }
n: longint;
imax, dmax: longint;
f, g: text;
function Min(a,b: longint): longint;
begin if (a <= b) then Min := a else Min := b; end;
function Max(a,b: longint): longint;
begin if (a >= b) then Max := a else Max := b; end;
Trang 20read(f,v); { v – phan tu dau day a }
p[v] := 1; istart := 1; { chi so dau doan }
if (i-istart > dmax) then Hv(istart, i-1);
istart := p[v]+1; { Khoi tri dau doan moi }
Tim doan dai nhat gom cac
phan tu tao thanh mot hoan vi cua 1 k
Trang 21const char * fn = "hv1kmax.inp";
const char * gn = "hv1kmax.out";
int Min(int, int);
int Max(int, int);
void Hv(int, int); // Sliding window
// gom cac phan tu khac nhau doi mot
void Hv(int d, int c) {
int i, pmin = c+1, pmax = d-1;
Trang 22Cho dãy a gồm n số nguyên dương Hãy tìm đoạn dài nhất gồm các phần tử tạo thành một hoán vị của dãy
số nguyên dương liên tiếp s, s+1, ,k; 1 n, a i 100000
Dữ liệu vào: Tệp văn bản hvskmax.inp
Dòng đầu tiên: số n
Từ dòng thứ hai trở đi: dãy số a
Dữ liệu ra: Tệp văn bản hvskmax.out chứa 2 số:
imax chỉ số đầu tiên của đoạn dài nhất tìm được trong dãy a
dmax số phần tử của đoạn dài nhất
Các số trên cùng dòng cách nhau qua dấu cách
hvskmax.inp hvskmax.out Giải thích
Pha 2 Duỵệt dãy giá trị i = vmin vmax tạo thành một đoạn d gồm các giá trị xuất hiện liên tiếp trong dãy
a, xác định các giá trị pmin và pmax là vị trí xuất hiện đầu tiên và cuối cùng trong đoạn d Đoạn d là một
hoán vị của dãy số nguyên dương liên tiếp i, i+1, ,s khi và chỉ khi pmaxpmin+1 = si
Độ phức tạp
Chương trình Pascal
Chương trình CPP
/* DevC++: hv1kmax.cpp
Tim doan dai nhat gom cac
phan tu tao thanh mot hoan vi cua s k
*/
Trang 23#include <fstream>
#include <iostream>
using namespace std;
// D A T A A N D V A R I A B L E S
const char * fn = "hvskmax.inp";
const char * gn = "hvskmax.out";
int Min(int, int);
int Max(int, int);
void Hv(int, int); // Sliding window
int Min(int a, int b) { return (a <= b) ? a : b; }
int Max(int a, int b) { return (a >= b) ? a : b; }
void Hv(int d, int c) {
int i, istart, q = 0, pmin = c+1, pmax = d-1;
int vmin, vmax;
case 0: // duyet doan toan 0
if (p[i] > 0) { // Khoi tri khi gap so khac 0 pmin = pmax = p[i];
Trang 25char p[mn]; // p[i] = 0=> i nguyen to
Trang 26Cho n số tự nhiên ai i = 1 n Hãy tìm đoạn dài nhất gồm các phần tử, trong đó hai phần tử kề nhau thì nguyên tố cùng nhau
const char * fn = "coprime.inp";
char p[mn]; // p[i] = 0=> i nguyen to
Trang 27Bài toán Euler N 28
Trong ma trận vuông n X n, n lẻ xuất phát từ ô giữa ghi lần lượt các số từ 1 n2 theo đường xoắn ốc theo chiều kim dồng hồ Với n = 5 tổng các phần tử trên 2 đường chéo là 101 Hãy tính tổng này với n = 1001
Trang 28cout << endl << Euler28(1001); // 669171001
cout << endl << " Fini";
cin.get();return 0 ;
}
Bài toán Euler N 52
Số x = 125874, và 2x = 251748 được tạo bởi cùng chữ số theo trật tự khác nhau Tìm số tự nhiên nhỏ nhất
x sao cho 2x, 3x, 4x, 5x và 6x có các chữ số ddoooi một khác nhau và là những hoán vị của x (142857)
Có trên 5 số thỏa tính chất trên hay ko?
Nếu ko đòi hỏi các chữ số khác nhau?
Với các số nguyên dương 1 4294967295 (232 − 1) có 10 số
Trang 29void GetDigits(UI x, char dg[]) {
for (int i = 0; i < 10; ++i) dg[i] = 0;
for (int i = 0; i < 10; ++i)
if (digits[i] != mark[i]) return false; return true;
for (int i = 2; i <= 6; ++i, x += d) {
if (!SameDigits(x)) return false;
f << endl << d << " : " << x;
Trang 30Tìm được 340059 số, số cuối là 4870843 với lặp n < 500
Ý tưởng: nếu p[1 n] , c = p[n] là danh sách n số nguyên tố đầu tiên và x là một số lẻ trong khoảng c c*c thì
x là nguyên tố khi và chỉ khi x không có ước nguyên tố trong khoảng 3 x
Xuất phát p = (2,3) − 2 số nguyên tố đầu tiên n = 2 là số lượng các số nguyên tố hiện tìm được
Find: xét các số lẻ x trong khoảng từ pn + 2 đến pn − 2 Chú ý, do pn là số lẻ nên pn lẻ Nếu x là ng tố thì thêm vào dãy
Để kiểm tra tính ng tố của x
for (UI i = 2; p[i] <= can; ++i)
if (x % p[i] == 0) return false;
return true;
}
Trang 31If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and
9 The sum of these multiples is 23
Find the sum of all the multiples of 3 or 5 below 1000
we get 3, 5, 6 and 9 The sum of these multiples is 23
Find the sum of all the multiples of 3 or 5 below 1000
*/
#include <iostream>
#include <stdio.h>
const int mn = 1001;
Trang 32while (a[n] < maxval) {
while (3*a[i3] <= a[n]) ++i3;
cout << endl << Euler01(1000); // 3822
cout << endl << " Fini";
cin.get();
return 0 ;
}
Each new term in the Fibonacci sequence is generated by adding the previous two terms
By starting with 1 and 2, the first 10 terms will be:
Trang 33fi+2 = fi + fi+1 = fi + fi−1 + fi = 2fi + fi−1 (lẻ),
fi+3 = f i+1 + fi+2 = fi−1 + fi + 2fi + fi−1 = 3fi + 2fi−1 = 2fi+2 − fi (chẵn)
Ta sử dụng biến odd để lưu số lẻ và biến even lưu số chẵn sát sau odd Ta có,
Khởi trị: odd = 1; even = 2;
Bước tiếp theo: odd = 2*even + odd; even = 2*odd – even;
1, 2, 3, 5, 8, 13, 21, 34, 55, 89,
Find the sum of all the even-valued terms in the sequence
which do not exceed four million
*/
#include <iostream>
#include <stdio.h>
using namespace std;
int Euler02(int maxval) {
int odd = 1, even = 2;
int s = even;
while (even < maxval) {
odd = 2*even + odd;
even = 2*odd - even;
cout << endl << Euler02(4000000); // 4613732
cout << endl << " Fini";
cin.get();
return 0 ;
}
Trang 34Euler03
The prime factors of 13195 are 5, 7, 13 and 29
What is the largest prime factor of the number 600851475143 ?
Khởi trị với danh sách chứa k = 2 số nguyên tố p[1] = 2; p[2] = 3 Hàm NextPrime(k) tìm số nguyên tố sát sau sô nguyên tố p[k] Gọi hàm NextPrime nhiều lần đến khi thu được số nguyên tố p[k] > n DuThe prime factors of 13195 are 5, 7, 13 and 29
What is the largest prime factor of the number 600851475143 ? (6857)
The prime factors of 13195 are 5, 7, 13 and 29
What is the largest prime factor of the number
for (int i = 1; p[i] <= ys; ++i)
if (fmodl(y,p[i]) == 0.0) return false;
return true;
}
Trang 35// Given a list consequenced first k prime numbers // p[1 k] = (3,5,7, p[k]), k > 1
// Find the next prime number (after p[k])
// Find consequenced prime numbers not greater than sqrt(n)
Trang 36// so n is a prime number We return n
if (imax == 0) return n; // n is a prime number
return (n < p[imax]) ? p[imax] : n;
float d = difftime(t2,t1);// sec
cout << endl << " Time: " << d << " sec " << endl; cout << "\n\n The result: " << r;
cout << endl << " Fini";
Find the largest palindrome made from the product of two 3-digit numbers
*/
#include <iostream>
#include <stdio.h>;