[USACO 1.4.3] Arithmetic Progressions

[题目描述]

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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值