[題目描述]
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.