[编程题] 素数对
时间限制:1秒
空间限制:32768K给定一个正整数,编写程序计算有多少对质数的和等于输入的这个正整数,并输出结果。输入值小于1000。如,输入为10, 程序应该输出结果为2。(共有两对质数的和为10,分别为(5,5),(3,7))
输入描述:
输入包括一个整数n,(3 ≤ n < 1000)
输出描述:
输出对数
输入例子1:
10
输出例子1:
2
解析:
求素数问题,一般用筛法,有线性筛法、快速线性筛法,欧拉筛法等。
线性筛法:
偶数(除2外)肯定不是素数,刚开始默认奇数都是素数。
当找到一个素数,显然这个素数乘以另一个数后都是合数,因此可以筛掉。筛的时候从i*i开始,步长为2i,这样比从2i开始筛要快。
分析发现,当数字非常大时,这种方法会重复筛出合数,影响效率。
快速线性筛法、欧拉筛法
快速线性法不会重复筛出一个数。
C++代码实现:
线性筛法版:
#include <iostream>
#include <math.h>
using namespace std;
bool primeDict[1001]={false};
void prime()
{
primeDict[2] = true;
int upper = sqrt(1000);
for(int i=3; i<=1000; i+=2)
primeDict[i] = true; //假设所有奇数是质数
for(int i=3; i<upper; i+=2) {
if(primeDict[i]==true){
for(int j=i*i; j<=1000; j+=2*i){ //从i*i开始排除,比i*2开始要快
primeDict[j] = false;
}
}
}
}
int main()
{
int n,count=0;
cin>>n;
if(n<3 || n>1000)
return 0;
int upbound = n>>1;
prime();
for(int i=2; i <= upbound; i++) {
if(primeDict[i] && primeDict[n-i])
count++;
}
cout<<count;
return 0;
}
欧拉筛法
#include <iostream>
#include <math.h>
using namespace std;
bool primeDict[1001]={false};
int primes[1001] = {0};
int pcount = 0;
void prime()
{
primes[pcount++]=2;
primeDict[2]=true;
for(int i=3; i<=1000; i+=2)
primeDict[i]=true;
for(int i=3; i<1000; i+=2) {
if(primeDict[i]==true){
primes[pcount++] = i;
}
for(int j=0; j<pcount && i*primes[j]<=1000; j++) {
primeDict[i*primes[j]] = false;
if(i%primes[j]==0) //关键
break;
}
}
}
int main()
{
int n,count=0;
cin>>n;
if(n<3 || n>1000)
return 0;
int upbound = n>>1;
prime();
for(int i=2; i <= upbound; i++) {
if(primeDict[i] && primeDict[n-i])
count++;
}
cout<<count;
return 0;
}
[编程题] geohash编码
时间限制:1秒
空间限制:32768K
geohash编码:geohash常用于将二维的经纬度转换为字符串,分为两步:第一步是经纬度的二进制编码,第二步是base32转码。
此题考察纬度的二进制编码:算法对纬度[-90, 90]通过二分法进行无限逼近(取决于所需精度,本题精度为6)。注意,本题进行二分法逼近过程中只采用向下取整来进行二分,针对二分中间值属于右区间。算法举例如下: 针对纬度为80进行二进制编码过程:
1) 区间[-90, 90]进行二分为[-90, 0),[0, 90],成为左右区间,可以确定80为右区间,标记为1;
2) 针对上一步的右区间[0, 90]进行二分为[0, 45),[45, 90],可以确定80是右区间,标记为1;
3) 针对[45, 90]进行二分为[45, 67),[67,90],可以确定80为右区间,标记为1;
4) 针对[67,90]进行二分为[67, 78),[78,90],可以确定80为右区间,标记为1;
5) 针对[78, 90]进行二分为[78, 84),[84, 90],可以确定80为左区间,标记为0;
6) 针对[78, 84)进行二分为[78, 81), [81, 84),可以确定80为左区间,标记为0;
输入描述:
输入包括一个整数n,(-90 ≤ n ≤ 90)
输出描述:
输出二进制编码
输入例子1:
80
输出例子1:
111100
C++代码实现:
#include <iostream>
using namespace std;
int main()
{
int n,count=6;
cin>>n;
if(n<-90 || n>90)
return 0;
int left=-90,right=90,mid=0;
while(count>0) {
mid = (left+right)/2;
if(n<mid){
right = mid;
cout<<0;
}
else{
cout<<1;
if(n>mid)
left = mid;
else{
while(--count>0)
cout<<0;
break;
}
}
count--;
}
return 0;
}
[编程题] 编码
时间限制:1秒
空间限制:32768K
假定一种编码的编码范围是a ~ y的25个字母,从1位到4位的编码,如果我们把该编码按字典序排序,形成一个数组如下: a, aa, aaa, aaaa, aaab, aaac, … …, b, ba, baa, baaa, baab, baac … …, yyyw, yyyx, yyyy 其中a的Index为0,aa的Index为1,aaa的Index为2,以此类推。 编写一个函数,输入是任意一个编码,输出这个编码对应的Index.
输入描述:
输入一个待编码的字符串,字符串长度小于等于100.
输出描述:
输出这个编码的index
输入例子1:
baca
输出例子1:
16331
解析:
以a开头的编码有:
1(a) + 25(a_) + 25*25(a_ )+25*25*25(a _ _) = 16276
即,a开头编码范围为[0,16275]
因此,所有的编码数为:25*16276 = 406900
其他字母开头编码范围可以依次类推,比如y开头编码范围为[390624, 406899]
可以看出:
(1)已知a的索引,求b的索引:因为a到b之间隔了以下四种情况的字符串:a后跟2字符的串有25个(aa,ab,…ay),a后跟2字符的串有25*25个(aaa, aab, … ayy),a后面跟3字符的串有25*25*25个(aaaa,aaab,…ayyy),然后才是b,所以b的索引 = a的索引 + 25+25*25+25*25*25 + 1,加1是因为b排在a和中间的字符之后1个
(2)已知aa的索引,求ab的索引:同理,ab的索引 = aa索引 + 25 + 25* 25 + 1
(3)已知aaa的索引,求aab的索引:同理,aab的索引 = aaa索引 + 25 + 1
(4)已知aaaa的索引,求aaab的索引 = aaaa索引 + 1
因此,得出以下推导公式,字符串为str
index = (str[0]-a)*16267 + (str[1]-a)*651 + (str[3]-a)*26 + (str[4]-a)*1 + (str.length-1)。
C++代码实现:
[编程题] 游戏任务标记
时间限制:1秒
空间限制:32768K
游戏里面有很多各式各样的任务,其中有一种任务玩家只能做一次,这类任务一共有1024个,任务ID范围[1,1024]。请用32个unsigned int类型来记录着1024个任务是否已经完成。初始状态都是未完成。 输入两个参数,都是任务ID,需要设置第一个ID的任务为已经完成;并检查第二个ID的任务是否已经完成。 输出一个参数,如果第二个ID的任务已经完成输出1,如果未完成输出0。如果第一或第二个ID不在[1,1024]范围,则输出-1。
输入描述:
输入包括一行,两个整数表示人物ID.
输出描述:
输出是否完成
输入例子1:
1024 1024
输出例子1:
1
解析:
32个int可以看成是一个32*32的位图, 总共1024位,每一位代表一个数
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
…
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
计算某个数在第几行第几列:
row = (num-1)/32, col = (num-1)%32
设置第k位为1:a | (1 << k)
取第k为: a>>k & 1
C++代码实现
#include <iostream>
#include <stdio.h>
using namespace std;
int findTask(unsigned int task[32],int num)
{
int row = (num-1)/32;
int col = (num-1)%32;
return (task[row]>>(31-col)&1);
}
void setTask(unsigned int task[32],int num)
{
int row = (num-1)/32;
int col = (num-1)%32;
task[row] = task[row]|(1<<(31-col)); //设定某位为1
}
int main()
{
unsigned int task[32] = {0};
int a,b;
while(scanf("%d %d",&a,&b)) {
if(a<1 || a>1024 || b<1 || b>1024)
cout<<-1<<endl;
else if(a==b){
setTask(task,a);
cout<<1<<endl;
}
else{
setTask(task,a);
cout<<findTask(task,b)<<endl;
}
}
return 0;
}