题目描述:
一个等差数列是一个能表示成a, a+b, a+2b,…, a+nb (n=0,1,2,3,…)的数列。
在这个问题中a是一个非负的整数,b是正整数。写一个程序来找出在双平方数集合(双平方数集合是所有能表示成p的平方 + q的平方的数的集合,其中p和q为非负整数)S中长度为n的等差数列。(翻译来源:NOCOW)
时间限制: 5 秒
INPUT FORMAT:
(file ariprog.in)
第一行: N(3<= N<=25),要找的等差数列的长度。
第二行: M(1<= M<=250),搜索双平方数的上界0 <= p,q <= M。
OUTPUT FORMAT:
(file ariprog.out)
如果没有找到数列,输出“NONE”。
如果找到了,输出一行或多行, 每行由二个整数组成:a,b。 a为等差数列的第一个值,b为等差数列的公差。
这些行应该先按b排序再按a排序。
所求的等差数列将不会多于10,000个。
SAMPLE INPUT
5
7
SAMPLE OUTPUT
1 4
37 4
2 8
29 8
1 12
5 12
13 12
17 12
5 20
2 24
解题思路:
这道题的主要思想就是暴力枚举,将所有的情况枚举出来并判断,难点就是如何剪枝。如果不剪枝的话第7组开始时间就超出了。我用a数组来保存所有的“bisquares”,通过模拟程序运行,我们会发现当当前等差数列的最后一项(a[i]+(a[j]-a[i])*(n-1))大于最大值时,j更大时不可能再有结果了(显而易见),通过这个思路可以减去大量的枝。下面是代码。
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
int a[150000],v[150000],m,n;
typedef struct Ans {
int x;
int y;
} Ans;
Ans ans[150000];
int cmp(const void *p,const void *q){
Ans *pp=(Ans *)p;
Ans *qq=(Ans *)q;
return pp->y-qq->y;
}
int acmp(const void *a,const void *b){
return *(int *)a-*(int *)b;
}
int main() {
FILE *fin = fopen ("ariprog.in", "r");
FILE *fout = fopen ("ariprog.out", "w");
fscanf(fin,"%d",&n);
fscanf(fin,"%d",&m);
int i,j,k,count=0,count1=0;
for(i=0;i<=m;i++){//枚举所有bisquares,并定义哈希表
for(j=0;j<=m;j++){
if(!v[i*i+j*j]){
a[count++]=i*i+j*j;
v[i*i+j*j]=1;
}
}
}
qsort(a,count,sizeof(a[0]),acmp);
for(i=0;i<count;i++){
for(j=i+1;j<count;j++){
if(a[i]+(a[j]-a[i])*(n-1)>m*m*2)break; //剪枝
for(k=n-1;k>=2;k--){//判断是否符合条件
if(!v[a[i]+(a[j]-a[i])*k])break;
}
if(k==1){
ans[count1].x=a[i];
ans[count1++].y=a[j]-a[i];
}
}
}
qsort(ans,count1,sizeof(ans[0]),cmp);
if(count1==0)fprintf(fout,"NONE\n");
else
for(i=0;i<count1;i++)
fprintf(fout,"%d %d\n",ans[i].x,ans[i].y);
exit(0);
}