昨天无意间碰到一道ACM题目“再次寻找外星人”,ACM的题目做得并不多,不过有个朋友倒是参加比赛,拿奖拿到手软。这道题目之所以单独拿出来研究分析,其中是有很多缘由的。本人在里面也受到了很多的启示。看到题目的第一眼,从一个初级思考者的角度,本题的思路应该属于:整数规划求最优解。通过设置最优解范围并通过剪枝构造最优解。基于上述思想写出了第一段代码。
#include <stdio.h>
int prime(int a) {
if(a == 2)
return 1;
if(a%2 == 0)
return 0;
else {
int i;
for( i=3;i*i<=a;i+=2)
{
if (a%i==0)
break;
}
if(i*i>a)
return 1;
else
return 0;
}
}
int main() {
int m,a,b;
scanf("%d%d%d",&m,&a,&b);
while(m!=0 && a!=0 && b!=0) {
int last_p=0,last_q=0;
int max = 0;
int p,q;
for(q=m/2;q>=2;q--) {
if(prime(q)) {
if(2*q >max && 2*b>=a*q) {
max = 2*q;
last_p = 2;
last_q = q;
break;
}
}
}
for(p=3;p*p<=m;p+=4) {
if(prime(p)) {
for(q=m/3;q>=p;q--) {
if(prime(q)) {
if(p*q >max && p*b>=a*q && p*q <= m) {
max = p*q;
last_p = p;
last_q = q;
break;
}
}
}
}
}
printf("%d %d\n",last_p,last_q);
scanf("%d%d%d",&m,&a,&b);
}
return 0;
}
上述代码虽然样例测试全部通过,但是提交之后得到的结果确实WA,同时还有超时的提示。由于上述代码也是做了一定的优化的基础上(素数的判定,循环体的优化)仍然出现超时的结果,说明求解的思路不正确。由于做得ACM题目不是很多,自己对于程序的直觉也不是太好,尤其是Time Limit: 1000MS Memory Limit: 65536K没有一个大体的概念,上述代码中最为浪费时间的地方就是对于素数的判断占用了大量的时间,同时出现了很强的重复。经过短暂思考后还是决定使用一开始的想法:首先构造素数表后再求解。
#include <stdio.h>
#define N 10000
int primelist[1400]={0},num=0;
int main() {
int i,j;
bool prime[N]={0};
for(i=2; i<N; i++)
if(prime[i]==0) {
primelist[num++]=i;
for(j=i+i; j<N; j+=i)
prime[j]=1;
}
int m,a,b;
double w;
scanf("%d%d%d",&m,&a,&b);
while(m!=0 && a!=0 && b!=0) {
int last_p=0,last_q=0;
int max = 0;
w=a/double(b);
int p,q;
for(i=0;i<num;i++) {
p = primelist[i];
if(p>m)
break;
else {
for(int j=i;j<num;j++) {
q = primelist[j];
if(q>m || (p/double(q))<w || p*q > m)
break;
if(p*q >max) {
max = p*q;
last_p = p;
last_q = q;
}
}
}
}
printf("%d %d\n",last_p,last_q);
scanf("%d%d%d",&m,&a,&b);
}
return 0;
}
将上述代码提交后程序获得通过。Memory: 320K Time: 94MS。上述代码是经过多次修改后的结果,期间出现了很多次的WA,虽然测试数据大部分都获得通过以及程序的思路都正确,但是仍然仍旧出现了WA,后来经过思考才发现问题是自己素数表的规模太小造成的,本文是构造出了1400个素数才获得通过。
本程序所得到的启示:
- 对于素数的问题,采用逐次判断的效率是极其低下的,同时构造素数表也有较为简单且固定的方法。
- 对于程序寻找最优解的边界设置以及剪枝条件都是需要仔细斟酌的一个比较差的剪枝条件可能会导致WA的出现,同时一个较好的剪枝条件或许能够极大程度的提高的程序的运行效率。