您的位置:首页 > 其它

51 nod 1056 最长等差数列 V2

2017-10-15 17:56 274 查看
1056 最长等差数列 V2

基准时间限制:8 秒 空间限制:131072 KB 分值: 1280 难度:9级算法题

N个不同的正整数,从中选出一些数组成等差数列。 

例如:1 3 5 6 8 9 10 12 13 14
等差子数列包括(仅包括两项的不列举)
1 3 5
1 5 9 13
3 6 9 12
3 8 13
5 9 13

6 8 10 12 14

其中6 8 10 12 14最长,长度为5。

现在给出N个数,你来从中找出一个长度 >= 200 的等差数列,如果没有,输出No Solution,如果存在多个,输出最长的那个的长度。

Input
第1行:N,N为正整数的数量(1000 <= N <= 50000)。
第2 - N+1行:N个正整数。(2<= A[i] <= 10^9)
(注,真实数据中N >= 1000,输入范例并不符合这个条件,只是一个输入格式的描述)


Output
找出一个长度 >= 200 的等差数列,如果没有,输出No Solution,如果存在多个,输出最长的那个的长度。


Input示例
10
1
3
5
6
8
9
10
12
13
14


Output示例
No Solution


提供一个可能是正解的做法吧(源自本题讨论区),也就是@李陶冶 提到的分治做法,来自Jeff Erickson在1999年发表的一篇短论文 Finding longest arithmetic progressions 。

这个论文的做法基于一个结论:大小为n的集合里长度至少为k的不同等差数列的数量是O(n*n/k*k)的。从这个结论入手可以尝试构造出答案。

首先要明确等差数列的定义。

一个等差数列可以用首项、公差和项数的三元组(first,delta,length)表示,当然这个first也可以换成末项last,取决于你的算法。
我们要考虑的等差数列要尽量用少的信息表示所有的可能,也就是说提取出来的等差数列之间不能有相互包含的关系,例如(2,3,4)和(2,3,5)就冗余了,而(2,3,4)和(5,3,4)也是冗余的,因为当它们出现在同一个序列里时,(2,3,5)也是存在的。

因此我们只需要考虑满足(first-delta)这个数字不存在,并且(last+delta)这个数字不存在的等差数列。



接下来要发现这样的等差数列在k充分大时是不多的。

定义集合里的两个元素是足够相邻的,当且仅当它们的排名之差不超过n/(k-1)。由鸽巢原理可知,一个长度为k的等差数列必然包含至少(k-1)/2对足够相邻的元素(考虑等差数列里排名最小的和排名最大的那两个元素)。

而集合里足够相邻的元素对数不超过n*n/2(k-1),我们所求的不同等差数列也不会包含相同的一对足够相邻的元素,所以我们能得到的等差数列数量是不超过n*n/(k-1)*(k-1)的。

最后要利用这个性质来构造相应的算法。

利用数量不多的性质,我们可以尝试找出所有长度至少为 200 的等差数列,从中选取最长的那个。

注意到上面的证明利用到了集合的大小,类似地也可以证明,这样的等差数列必然有⌊k/2⌋项在前⌊n/2⌋小的元素集合里,或者有⌊k/2⌋项在前⌈n/2⌉大的元素集合里。而集合大小减半同时数列项数减半对数列数量的影响是不大的,所以可以尝试对集合分治,分别找出更小情况的解,然后放到大的集合里尝试向左向右O(k)扩张,再进行去重,从而得到当前情况的解。

不妨设T(n,k)表示从大小为n的集合里提取长度至少为k的不同等差数列的复杂度,则有T(n,k)=2T(n/2,k/2)+O((n*n/k*k)k)⇒T(n,k)=O(n*n/k
log n/k)。



实现上可能要注意几个细节:

1. 需要实现原集合的hash来保证扩张的复杂度。

2. 当k=O(log n)时,可以采用O(n*n)的dp直接计算,效率更高,此时递归层数是O(log k)的。

3. k实际上可以动态变化,从而优化常数,本题数据好像不太强,稍微写错点地方也不会出问题。

4. 论文中提到寻找最长等差数列的时候依次选取k=n,n/2,n/4,…,对于本题可以直接选取k=200。

———————————————————————————————————————————————

但因为种种原因,本题可以玄学搜索+各种剪枝用O(n*n)的时间复杂度完成。搜索方法:枚举第一个数,然后枚举公差,用hash判断可行性。

Code:
uses math;
const m=10000000;
var
num,k,i,n,minans,maxans,len,ans,j,d:longint;
a,hash:array[0..10000005] of longint;t:int64;
procedure sort(l,r:longint);
var i,j,x,y:longint;
begin
i:=l;j:=r;x:=a[(l+r) div 2];
repeat
while a[i]<x do inc(i);
while x<a[j] do dec(j);
if not(i>j) then
begin
y:=a[i];a[i]:=a[j];a[j]:=y;
inc(i);dec(j);
end;
until i>j;
if l<j then sort(l,j);
if i<r then sort(i,r);
end;
function hashhash(x:longint):longint;
var k:longint;
begin
k:=x mod m;
if k=0 then k:=k+m;
while (hash[k]<>0)and(hash[k]<>x) do k:=k mod m+1;
exit(k);
end;
function find(x:longint):boolean;
begin
if hash[hashhash(x)]=x then exit(true);
exit(false);
end;
begin
read(n);
minans:=maxlongint;
for i:=1 to n do
begin
read(a[i]);
if a[i]>maxans then maxans:=a[i];
if a[i]<minans then minans:=a[i];
end;
sort(1,n);
a[0]:=a[1]-1;
for i:=1 to n do hash[hashhash(a[i])]:=a[i];
for i:=1 to n do
for j:=i+1 to n do
begin
d:=a[j]-a[i];
k:=a[j];len:=2;
t:=a[i]+ans*d;
if (t>maxans)or(t<minans) then break;
while find(k+d) do
begin
k:=k+d;
inc(len);
end;
if len>ans then ans:=len;
end;
if ans<200 then writeln('No Solution') else
writeln(ans);
end.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: