[题目描述]
Arithmetic Progressions
等差数列
一个等差数列是一个能表示成a, a+b, a+2b,..., a+nb (n=0,1,2,3,...)
在这个问题中a是一个非负的整数,b是正整数。
写一个程序来找出在双平方数集合S中长度为n的等差数列。
双平方数集合是所有能表示成p2+q2的数的集合。
PROGRAM NAME: ariprog
INPUT FORMAT
第一行: | N(3<= N<=25),要找的等差数列的长度。 |
第二行: | M(1<= M<=250),搜索双平方数的上界0 <= p,q <= M。 |
SAMPLE INPUT (file ariprog.in)
5
7
OUTPUT FORMAT
如果没有找到数列,输出`NONE'。
如果找到了,输出一行或多行, 每行由于二个整数组成:a,b
这些行应该先按b排序再按a排序。
将不会有只多于10,000个等差数列。
SAMPLE OUTPUT (file ariprog.out)
1 4
37 4
2 8
29 8
1 12
5 12
13 12
17 12
5 20
2 24
[解题思路]
比较简单的想法就是先M^2的找出S集合,然后枚举首项元素q[i],再另外枚举q[j],用q[j]-q[i]作为公差b,然后判断是否可行,最后排序输出。
但实际效果并不理想,5s的时限,在第8个点TLE了...
再加剪枝,记录S集合中的最大数为up,枚举公差之后可以O(1)的判断是否有可行解,a+b*(n-1) > up 则直接break掉,效率可以直线上升,0.8s-AC...
话说回来,搜索的剪枝是很重要的,我还得多加练习,分析清楚整个搜索程序的复杂度,加上适当的剪枝。
[Code]
{ ID: zane2951 PROG: ariprog LANG: PASCAL } program ariprog; var v:array[0..125111] of boolean; q:array[0..125111] of longint; x,y:array[0..10001] of longint; ce,i,j,n,m,top,tp,up:longint; //-----------qs------------ procedure qs(s,t:longint); var i,j,ce,tmp:longint; begin i:=s; j:=t; ce:=q[(i+j)>>1]; repeat while q[i]<ce do inc(i); while q[j]>ce do dec(j); if i<=j then begin tmp:=q[i]; q[i]:=q[j]; q[j]:=tmp; inc(i); dec(j); end; until i>j; if i<t then qs(i,t); if s<j then qs(s,j); end; //----------qqss----------- procedure qqss(s,t:longint); var i,j,cex,cey,tmp:longint; begin i:=s; j:=t; cey:=y[(i+j)>>1]; cex:=x[(i+j)>>1]; repeat while (y[i]<cey) or (y[i]=cey) and (x[i]<cex) do inc(i); while (y[j]>cey) or (y[j]=cey) and (x[j]>cex) do dec(j); if i<=j then begin tmp:=x[i]; x[i]:=x[j]; x[j]:=tmp; tmp:=y[i]; y[i]:=y[j]; y[j]:=tmp; inc(i); dec(j); end; until i>j; if i<t then qqss(i,t); if s<j then qqss(s,j); end; //---------check----------- function check(a,d:longint):boolean; var i:longint; begin for i:=2 to n-1 do if not v[a+d*i] then exit(false); exit(true); end; //----------main----------- begin assign(input,'ariprog.in'); reset(input); assign(output,'ariprog.out'); rewrite(output); readln(n); readln(m); top:=0; up:=-1; for i:=0 to m do for j:=i to m do begin ce:=i*i+j*j; if not v[ce] then begin if ce>up then up:=ce; v[ce]:=true; inc(top); q[top]:=ce; end; end; qs(1,top); tp:=0; for i:=1 to top do for j:=i+1 to top do begin ce:=q[j]-q[i]; if q[i]+ce*(n-1)>up then break; if check(q[i],ce) then begin inc(tp); x[tp]:=q[i]; y[tp]:=ce; end; end; if tp=0 then writeln('NONE') else begin qqss(1,tp); for i:=1 to tp do writeln(x[i],'':1,y[i]); end; close(input); close(output); end.