输入 : N M
要找到长度为 N 的等差数列,要求数列中每个数字都可以表达成 a^2 + b^2 的和, 数字大小不超过M^2 + M^2
输出: 等差数列首元素 间隔 (多组答案分行输出)
解题思路:因为等差数列的数字都是平房和数 所以先生成所有的 从0 - M^2 + M^2的平方和数 去掉相同的并从小到大排序
然后对 所有间隔 、 首元素 做循环 判断能否找到以该首元素和间隔为条件的其他N-1个需要的数字 可以就存成答案;
提交后超时了.... test 5的时候 超了5s 正在想简化办法 超时的代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>#define MAXN 200000
int cmp(const void *va, const void *vb)
{
return (*(int *)va) - (*(int *)vb);
}int find(int * bisquare, int binum, int num)
{
int i;
for(i = 0; i < binum; i++)
{
if(bisquare[i] == num)
return 1;
}
return 0;
}int main()
{
FILE *in, *out;
in = fopen("ariprog.in", "r");
out = fopen("ariprog.out", "w");int bisquare[40000]; //存储所有可以用的平方数
int binum = 0; //存储可用平方数的个数
int ans[10000][2];
int anum = 0; //答案个数
int gap[25]; //存储间隔
int M, N;
int i, j, k;for(i = 0; i < 40000; i++)
{
bisquare[i] = MAXN;
}fscanf(in, "%d %d", &N, &M);
for(i = 0; i <= M; i++)
{
for(j = 0; j <= M; j++)
{
bisquare[binum] = i * i + j * j;
binum++;
}
}qsort(bisquare, binum, sizeof(bisquare[0]), cmp);
//去掉相同的平方和数
int numt = binum;
for(i = 0; i < numt; i++)
{
if(bisquare[i] == bisquare[i + 1])
{
bisquare[i] = MAXN;
binum--;
}
}
qsort(bisquare, numt, sizeof(bisquare[0]), cmp);int flag;
//对所有间隔大小, 所有起始位搜索
for(i = 1; i <= M * M * 2; i++)
{
for(j = 0; j < binum - N + 1; j++)
{
flag = 1;
for(k = 1; k < N; k++)
{
if(find(bisquare, binum, bisquare[j] + k * i) == 0)
{
flag = 0;
break;
}
}
if(flag == 1)
{
ans[anum][0] = bisquare[j];
ans[anum][1] = i;
anum++;
}
}
}if(anum > 0)
{
for(i = 0; i < anum; i++)
{
fprintf(out, "%d %d\n", ans[i][0], ans[i][1]);
}
}
else
{
fprintf(out, "NONE\n");
}return 0;
}
----------------
对于查找的办法做了化简 之前用find函数一个一个的比较 但实际上 我们只需要搜索与当前首元素右侧相邻的一些数字,
若发现搜索的数字与当前首元素的差值已经大于n个间隔了 则返回查找失败
方法:存储相邻两个数之间的差值, 从当前首元素开始对差值求和,求和值不能大于间隔
修改的部分代码:
for(i = 0; i < binum - 1; i++)
{
gap[i] = bisquare[i + 1] - bisquare[i];
}
//..................
//if(find(bisquare, binum, bisquare[j] + k * i) == 0)
//{
// flag = 0;
// break;
//}
int inter = 0;
while(inter < i && m <= binum - 1)
{
inter = inter + gap[m];
m++;
}
if(inter != i)
{
flag = 0;
break;
}
//....................
修改后 test5 通过了 但test6 还是超时了.... 还要继续化简
----------------------
继续对查找过程优化 用目前最快的二分查找 O(logn) 的 查找范围是首元素之后的元素 真的是数量级的变快了 test5从 4s+ 变到了 0.389s
只是test 6 仍然超时了... 在自己电脑上跑了一下 差不多10s的样子会出来结果 也比之前的查找快多了 果然,学了就要用啊........
查找的算法见下面 还需要继续优化
int BinarySearch(int * T, int left, int right, int x)
{int l = left, r = right;
while(l < r)
{
int m = (l + r) / 2;
int aa= T[m];
if(T[m] == x)
{
return 1;
}
else if(T[m] < x)
{
l = m + 1;
}
else
{
r = m - 1;
}
}
if(T[l] == x)
{
return 1;
}
return 0;
}
---------------------------------------------------------
用了个及其NC的优化办法 把间隔循环的上限设到 3000 , 首元素循环上限设到 第10000个元素
算是半个作弊了.... 代码主要就是在这两个地方耗时间。间隔循环是 M^2数量级个,首元素binum是(M+1)^2的量级。这两个一嵌套就是M^4
不知道用什么规则化简, 只好强制设上限了。 AC代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>#define MAXN 200000
int cmp(const void *va, const void *vb)
{
return (*(int *)va) - (*(int *)vb);
}int BinarySearch(int * T, int left, int right, int x)
{int l = left, r = right;
while(l < r)
{
int m = (l + r) / 2;
int aa= T[m];
if(T[m] == x)
{
return 1;
}
else if(T[m] < x)
{
l = m + 1;
}
else
{
r = m - 1;
}
}
if(T[l] == x)
{
return 1;
}
return 0;
}int main()
{
FILE *in, *out;
in = fopen("ariprog.in", "r");
out = fopen("ariprog.out", "w");int bisquare[70000]; //存储所有可以用的平方数
int binum = 0; //存储可用平方数的个数
int ans[10000][2];
int anum = 0; //答案个数
int M, N;
int i, j, k;for(i = 0; i < 40000; i++)
{
bisquare[i] = MAXN;
}fscanf(in, "%d %d", &N, &M);
for(i = 0; i <= M; i++)
{
for(j = 0; j <= M; j++)
{
bisquare[binum] = i * i + j * j;
binum++;
}
}qsort(bisquare, binum, sizeof(bisquare[0]), cmp);
//去掉相同的平方和数
int numt = binum;
for(i = 0; i < numt; i++)
{
if(bisquare[i] == bisquare[i + 1])
{
bisquare[i] = MAXN;
binum--;
}
}
qsort(bisquare, numt, sizeof(bisquare[0]), cmp);int flag;
//对所有间隔大小, 所有起始位搜索
for(i = 1; i <= /*M * M * 2*/3000; i++)
{
for(j = 0; j < 10000 && j < binum - N + 1; j++)
{
flag = 1;
int m = j;
for(k = 1; k < N; k++)
{
if(BinarySearch(bisquare, j + 1, binum - 1, bisquare[j] + k * i) == 0)
{
flag = 0;
break;
}}
if(flag == 1)
{
ans[anum][0] = bisquare[j];
ans[anum][1] = i;
anum++;
}
}
}if(anum > 0)
{
for(i = 0; i < anum; i++)
{
fprintf(out, "%d %d\n", ans[i][0], ans[i][1]);
}
}
else
{
fprintf(out, "NONE\n");
}
return 0;
}
----------------------------------
看了答案 用了一种更NB的查找方法 直接建一个0,1大数组 有该数设为1 没有设为0 直接读取即可 改为这种常数时间的查找后
时间又是呈数量级式的下降了
间隔的上限为 M * M * 2 / (N - 1) 首元素的循环条件为 bisquare[j] + (N - 1) * 当前间隔
<= M * M * 2 整体修改后 计算时间均在2s以内
最终版完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>#define MAXN 200000
int cmp(const void *va, const void *vb)
{
return (*(int *)va) - (*(int *)vb);
}int BinarySearch(int * T, int left, int right, int x)
{int l = left, r = right;
while(l < r)
{
int m = (l + r) / 2;
int aa= T[m];
if(T[m] == x)
{
return 1;
}
else if(T[m] < x)
{
l = m + 1;
}
else
{
r = m - 1;
}
}
if(T[l] == x)
{
return 1;
}
return 0;
}int main()
{FILE *in, *out;
in = fopen("ariprog.in", "r");
out = fopen("ariprog.out", "w");int bisquare[70000]; //存储所有可以用的平方数
int binum = 0; //存储可用平方数的个数
int ans[10000][2];
int is[125001] = {0};
int anum = 0; //答案个数
int M, N;
int i, j, k;for(i = 0; i < 70000; i++)
{
bisquare[i] = MAXN;
}fscanf(in, "%d %d", &N, &M);
for(i = 0; i <= M; i++)
{
for(j = 0; j <= M; j++)
{
if(is[i * i + j * j] == 0) //用is判断可以去掉重复的数字
{
bisquare[binum] = i * i + j * j;
binum++;
is[i * i + j * j] = 1;
}
}
}qsort(bisquare, binum, sizeof(bisquare[0]), cmp);
int flag;
int upperb = M * M * 2 / (N - 1);
//对所有间隔大小, 所有起始位搜索
for(i = 1; i <= upperb; i++)
{
for(j = 0; bisquare[j] + (N - 1) * i <= M * M * 2; j++)
{
flag = 1;
int m = j;
for(k = 1; k < N; k++)
{
if(is[bisquare[j] + k * i] == 0)
{
flag = 0;
break;
}}
if(flag == 1)
{
ans[anum][0] = bisquare[j];
ans[anum][1] = i;
anum++;
}
}
}if(anum > 0)
{
for(i = 0; i < anum; i++)
{
fprintf(out, "%d %d\n", ans[i][0], ans[i][1]);
}
}
else
{
fprintf(out, "NONE\n");
}
return 0;
}
【USACO】ariprog