Mật mã

 

Cho dãy số nguyên ai, i=1,2,…,n. Dãy số này dùng để mã hoá các thông báo, mỗi thông báo có n bit. Nếu dãy bit của thông báo là t1,t2,…,tn (ti bằng 1 hoặc 0) thì nó được mã hoá thành số: S = t1a1 + t2a2 + … + tnan

Yêu cầu: Cho thông báo đã mã hoá và dãy số ai. Hãy giải mã thông báo.

Dữ liệu: Vào từ file văn bản cipher.inp:
- Dòng đầu tiên chứa số nguyên dương n
(5 <= n <= 30);
- Dòng thứ i trong n dòng tiếp theo chứa số nguyên
ai, tổng các ai không vượt quá 2×109
- Dòng thứ n+2 chứa số nguyên S – thông báo đã mã hoá,
S <=a1 + a2 + … + an.

Kết quả: Đưa ra file văn bản cipher.out thông báo đã giải mã dưới dạng chuỗi bit. Dữ liệu vào đảm bảo có nghiệm.

cipher.inp

24
19226985
123697
67356296
19721773
1113273
69335448
23680077
9029881
85168664
93676782
5253843
77616588
78572630
13375812
17199980
101508862
59248276
3505733
35790095
62028546
85726819
56462819
103373994
91757169
667509506

cipher.out

110001000101101100010101

Thuật toán

- Với n giới hạn, ta có thể giải quyết bài toán bằng phương pháp vét cạn: xét tất cả các trạng thái có thể có của dãy bit thông báo, với mỗi trạng thái, kiểm tra xem sau khi mã hoá có khớp với S đã cho trước hay không, dãy bit cho kết quả mã hoá trùng với S chính là dãy cần tìm.

- Dễ thấy, độ phức tạp để vét tất cả các xâu nhị phân có độ dài n là O(2n). Do đó với n lớn, thuật toán vét cạn cho chương trình chạy khá chậm. Dựa trên ý tưởng vét trên, ta có thể cải tiến thêm để chương trình chạy tốt hơn.

Chia xâu n bit thành 2 phần, phần 1 gồm n/2 bit đầu tiên, phần 2 gồm n/2 bit còn lại.

Trước tiên xét tất cả các trạng thái phần 1, có 2n/2 trạng thái (tối đa là 215) nên ta có thể lưu tất cả các trạng thái này lại, mỗi trạng thái lưu dưới dạng 1 số duy nhất là số mã hoá của phần 1.

Sau đó, vét tiếp các trạng thái của phần 2, tương tự phần 1, có tối đa 215 trạng thái, ứng với mỗi trạng thái cũng có 1 số mã hoá.

Dễ dàng nhận thấy, nếu tổng của số mã hoá của phần 1 và phần 2 bằng S, thì 2 trạng thái tương ứng ghép lại chính là trạng thái đúng của xâu n bit cần tìm. Bài toán đưa về việc tìm 2 số thuộc 2 dãy có tổng bằng số S cho trước. Ứng với mỗi số mã hoá của phần 2 tìm được, ta tìm nhị phân trên dãy số mã hoá của phần 1 đã lưu để xác định trạng thái cho tổng số mã hoá là S.

Như vậy ta đã thực hiện 2 lần bước vét các trạng thái của xâu nhị phân độ dài n/2, độ phức tạp là O(2n/2), và 2n/2 lần vét nhị phân trên mảng gồm 2n/2 phần tử, độ phức tạp là O(2n/2 x log2n/2). Độ phức tạp giảm đáng kể và cho chương trình chạy trong thời gian chấp nhận được.

Download chương trình (cipher.pas)
http://ity.vnuit.edu.vn/dethi/000002/index.htm

Dãy số trung bình

 

Xét dãy số nguyên không giảm S1, S2, …, Sn+1 (Si <= Si+1). Dãy m1, m2, …mn được xác định bởi công thức mi = (si + si+1)/2 (1<=i<=n) gọi là dãy trung bình của dãy S. Ở đây ta chỉ xét trường hợp mi là số nguyên. Cho trước dãy m, nhiệm vụ của bạn là tìm tất cả những dãy S nhận dãy m làm dãy trung bình

Input: Dữ liệu vào từ file Mean.inp gồm

     Dòng đầu tiên chứa số nguyên N (1<=N<=5 000 000)

     N dòng sau, dòng thứ i chứ số nguyên mi(1<=mi<=1 000 000 000)

Output: Dữ liệu ra ghi lên file Mean.out gồm một số nguyên duy nhất là số lượng dãy S thỏa yêu cầu đề bài mà bạn tìm được

Ràng buộc: Chương trình không được phép sử dụng quá 16MB bộ nhớ.

MEAN.INP MEAN.OUT

Thuật toán: Từ công thức mi = (si + si+1)/2 ta có mi >= si. Bản chất của bài toán thật ra là yêu cầu ta tìm tập giá trị của s1 mà thôi vì khi có s1 ta có thể tính ra toàn bộ dãy s theo công thức của dãy m: si+1 = 2mi-1-si.

Đặt s1 = a, ta có s2 = 2m1 – a <= m2 <=> a>= 2m1 – m2. Tương tự, với các bất đẳng thức s3<m3, s4<m4, …, sn < mn ta cũng suy ra được các giới hạn khác của a. Tuy nhiên, chỉ có hai dạng chung là:

      a >= x

      a <= y

Gọi Xmaxlà giá trị lớn nhất trong số các cận dưới của a, Ymin là giá trị nhỏ nhất trong số các cận trên của A. Khi đó, dãy số S thỏa yêu cầu đề bài là Ymin – Xmax + 1. Độ phức tạp của thuật toán chỉ là O(n)!

Download chương trình (mean.pas)
http://ity.vnuit.edu.vn/dethi/000003/index.htm

« Bài viết cũ hơn