Nguồn đề bài http://vn.spoj.com/problems/LCS2X/
1. Đề bài LCS2X spoj
Dãy C = c1, c2, .. ck được gọi là dãy con của dãy A = a1, a2, .., an nếu C có thể nhận được bằng cách xóa bớt một số phần tử của dãy A và giữ nguyên thứ tự của các phần tử còn lại, nghĩa là tìm được dãy các chỉ số 1 ≤ l1 < l2 < … < lk ≤ n sao cho c1 = a_l1, c2 = a_l2, …, ck = a_lk. Ta gọi độ dài của dãy là số phần tử của dãy.
Cho hai dãy A = a1, a2, …, am và B = b1, b2, …, bn Dãy C = c1, c2, …, ck được gọi là dãy con chung bội hai của dãy A và B nếu C vừa là dãy con của dãy A, vừa là dãy con của dãy B và thỏa mãn điều kiện 2 × ci ≤ c(i+1) (i = 1, 2, …, k – 1).
Yêu cầu
Cho hai dãy A và B. Hãy tìm độ dài dãy con chung bội hai có độ dài lớn nhất của hai dãy A và B.
Input
Dòng đầu tiên chứa T là số lượng bộ dữ liệu. Tiếp đến là T nhóm dòng, mỗi nhóm cho thông tin về một bộ dữ liệu theo khuôn dạng sau:
- Dòng đầu chứa 2 số nguyên dương m và n.
- Dòng thứ hai chứa m số nguyên không âm a1, a2, …, am mỗi số không vượt quá 10^9.
- Dòng thứ ba chứa n số nguyên không âm b1, b2, …, bn mỗi số không vượt quá 10^9.
- Các số trên cùng một dòng được ghi cách nhau ít nhất một dấu cách.
Giới hạn
- 30% số test có m, n <= 15.
- 30% số test khác có m, n <= 150.
- có 40% số test còn lại có m, n <= 1500.
Output
Ghi ra T dòng, mỗi dòng ghi một số nguyên là độ dài dãy con chung bội hai dài nhất của dãy A và B tương ứng với bộ dữ liệu vào.
Example
Input: 1 5 5 5 1 6 10 20 1 8 6 10 20 Output: 3
2. Hướng dẫn LCS2X spoj
Gọi F[i, j] là độ dài dãy con chung dài nhất của dãy A[1..i] và dãy B[1..j] thỏa điều kiện đề bài và A[i]=B[j].
Công thức truy hồi:
- Nếu A[i]<>B[j] thì F[i, j]=0
- F[i, j]= Max{F[u, v]/ u<i, v<j}+1 và thỏa điều kiện đề bài. Có nghĩa là tồn tại 2 chỉ số k, t sao cho Max{F[u, v]/ u<i, v<j}=F[k,t] và A[k]*2<=A[i]; B[t]*2<=B[j]
Nhận xét:
- Nếu cài đặt thông thường (sử dụng 4 vòng for i, j, u, v) độ phức tạp O(N4) chỉ giải quyết được 30% test.
- Nếu ta thấy dãy con chung ( các phần tử được chọn ở dãy A và B giống nhau) ta có thể giải quyết với độ phức tạp O(N3) cụ thể:
Tính F[i, j]= Max{F[i, v]/ v<j}+1 và thỏa điều kiện đề bài. Có nghĩa là tồn tại chỉ số t sao cho Max{F[i, v]/ v<j}=F[i,t] và B[t]*2<=A[i];
Với cách giải quyết này ta chỉ giải quyết được 60% test.
- Để giải quyết hết các test ta có thể tính trước như sau:
+ Với mỗi i ta gọi Max_T là Max{F[i, j] đã được tính}và thỏa điều kiện đề bài.
+ Với mỗi i ta gọi D[j] là Max{F[i, j] đã được tính}
+ Khi đó F[i, j]= Max_T +1 Khi A[i]=B[j]
+ Max_T được cập nhật khi A[i]>=B[j]*2
+ D[j] được cập nhật khi A[i]=B[j]
3. Code tham khảo LCS2X spoj (pascal, C++)
[sociallocker]
a. Code LCS2X spoj Pascal
uses math; const fi=''; nmax=1500+100; type data=longint; var f:text; n,m,test:data; a,b,d:array[0..nmax] of data; C:array[0..nmax,0..nmax] of data; gtmax,res:data; procedure QHD; var i,j:data; begin res:=0; for i:=0 to n do d[i]:=0; for i:=1 to m do begin gtmax:=0; for j:=1 to n do begin if a[i]=b[j] then c[i,j]:=gtmax+1; if a[i]>=2*b[j] then gtmax:=max(gtmax,d[j]); if a[i]=b[j] then begin res:=max(res,c[i,j]); d[j]:=max(d[j],c[i,j]); end; end; end; writeln(res); end; procedure docfile; var i,j:data; begin assign(f,fi); reset(f); readln(f,test); for i:=1 to test do begin read(f,m,n); for j:=1 to m do read(f,a[j]); for j:=1 to n do read(f,b[j]); QHD; end; close(f); end; begin docfile; end.
b. Code LCS2X spoj C++
include<stdio.h> #include<string.h> #include<algorithm> using namespace std; int main() { //freopen("test.inp","r",stdin); int t,m,n,i,j,a[1505],b[1505],c[1505],cur,prev; scanf("%d",&t); while(t--) { memset(c,0,sizeof(c)); scanf("%d%d",&m,&n); for(i=0;i<m;++i) scanf("%d",&a[i]); for(i=0;i<n;++i) scanf("%d",&b[i]); for(i=0;i<m;++i) { cur=0; for(j=0;j<n;++j) { prev=cur; if(2*b[j]<=a[i]) cur=max(cur,c[j]); if(a[i]==b[j]) c[j]=max(c[j],prev+1); } } cur=0; for(i=0;i<n;++i) cur=max(cur,c[i]); printf("%dn",cur); } }
[/sociallocker]