1.数的反序处理:数位
问题 B: 区间内的真素数
内存限制:128 MB时间限制:1.000 S
题目描述
找出正整数M和N之间(N不小于M)的所有真素数。
真素数的定义:如果一个正整数P为素数,且其反序也为素数,那么P就为真素数。
例如,11,13均为真素数,因为11的反序还是为11,13的反序为31也为素数。
输入
输入两个数M和N,空格间隔,1≤M≤N≤100000。
输出
按从小到大输出M和N之间(包括M和N)的真素数,逗号间隔。如果之间没有真素数,则输出No。
样例输入
10 35
样例输出
11,13,17,31
上一次写了素数筛模块,今天就会用到在这题;
当然本题最核心的还是如何获取数的逆序。在此不给出本题题解了,一本通都有.
拼装法
(这里面有一些方法是我自己为了方便命名的.)
新手村会用到的一些:
假设i是一个四位数,那么,(仅列举常见和方便的)
其千位:i/100/10;
其百位:i/100%10;
(/的优先级等同于%,*也一样,数学上易推知,与变量从左往右结合)
其十位:i%100/10
其个位:i%10==i%100%10
而一般又有这样的写法:
设两个整形变量:
h=i/100; w=i%100;
那么可简化为:
千位:h/10,百位h%10,十位w/10,个位w%10.
然后取反,各位乘1,10,100,1000即可。
但是这样有个坏处,如果给出的取值区间很大效率就会很低.所以,需要寻求更优解.
逐位移动法
这是一本通上介绍的一个好用的方法,并不使用数组。
int move(int n)
{
int s = 0;
while (n > 0)
{
s = s * 10 + (n % 10);
n /= 10;
}
return s;
}
我们再来回顾一下,n%10是数的个位,所以第一次循环s=个位;第二次循环个位x10+新的n%10,而这个新的n就是原来的n除以10取整后的结果,原来的n/10即等于(千百十)。如此做可以把低位向高位迭代,高位不断跟在低位后面,完成逆序排序。
于是,改变while循环条件及内容可以改变取位数区间,这里不再赘述,仅举一例比如获取数n的后三位逆序:
int move(int n)
{
int s = 0;
while (3--)
{
s = s * 10 + (n % 10);
n /= 10;
}
return s;
}
2.数的进制转换:
逐位移动法的变式
把这两个放在一起讲是因为他们的执行原理都很类似。
int turn(int x,int A)//A是目标进制数
{
int m = 1, xA = 0;
while (x != 0)//边界
{
if ((x % 10) < A)
{
xA += (x % 10) * m;
m *= A;
x /= 10;
}
}
return xA;
}
首先A进制数位不能大于等于A;然后进入循环,从个位开始,十位,百位...分别乘以1,A,A^2即可。它是依靠x%10和x/=10两大支撑代码逐位遍历完成的。难点仍然是逐位移动的算法。
当然如果说清楚和理解透彻了之后,这样的算法也是随手构造即得.
递归算法
那么有没有不用逐位遍历的算法呢?有的。虽然也是遍历,但这次可以使用整个数去遍历。
这就是递归算法,它通过数n每次对k取模,自身除以k,取得目标数最低位,次低位...遍历出整个目标数.
char d[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
int turn(int x,int A)
{
int r;
r = x % A;
x = x / A;
if (x != 0)
turn(x, A);
cout<< d[r];//用ASCII码转会比较麻烦,9和A之间隔了几个符号.
return 0;
}
int main()
{
int x, m;
cin >> x >> m;
turn(x, m);
cout << endl;
return 0;
}
核心是递归思想,当然和上述移位的做法也有共同之处,实际应用可自行灵活处理.