USACO-Section1.4 Arithmetic Progressions【暴力枚举】

题目描述:

一个等差数列是一个能表示成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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值