http://poj.org/problem?id=3126
译文
正如前文所说,eroengine突然喜爱上了质数,他对质数的爱已经到了魔怔的地步。现在他想到了一个小游戏,首先随机挑选两个四位的素数a,b。
游戏规则是:a可以通过改变某一位上的数字使其变成c,但只有当c也是四位的素数时才能进行这种改变。
eroengine只擅长抛出问题而不擅长解决问题,但他觉得你一定可以很轻松的算出a最少经过多少次变化使其变为b。
例如:
1033 -> 8179
1033
1733
3733
3739
3779
8779
8179
最少变换了6次。
考虑为poj 有些比较新的用法可能无法使用,请注意
Input
第一行输入整数T,表示样例数。 (T <= 100)
每个样例输入两个四位的素数a,b。(没有前导零)
Output
对于每个样例,输出最少变换次数,如果无法变换成b则输出"Impossible"。
Sample Input
3
1033 8179
1373 8017
1033 1033
Sample Output
6
7
0
思路
这道题目就是寻找一个四位素数到另一个四位素数的最小改变次数,每次只能改变一个数字,且改过后的数仍然是素数,由于会多次进行素数判断所以采用线性素数筛进行预处理会减少程序的运行时间
分析过后就简单了,首先打一张素数表,然后我们再通过bfs改变位数,直到变为最终数字,记录步数,改变的时候枚举0-9每一位数字就可以了
细节
- 判重
由于在BFS过程中可能会出现曾经出现过的数字导致死循环
所以增加一个标记数组进行判重
bool vis[N];/*判重*/
2.枚举
由于四位数的偶数一定不是素数
千位不能为零
所以在BFS的过程中 个位只枚举奇数 千位不能为零
3.设计时的思考
获取数字num的每一个数字时
1.转换成字符串再进行改变
字符串和整数之间的互相转化需要设计一系列的函数
2.通过整除和取模运算得到每一位上的数字
3.使用数组和循环结合来存储每一位的数字
实现过程以及分析
数学取模的方式得到每一位上的数字
T[0] = t / 1000;
T[1] = (t / 100) % 10;
T[2] = (t % 100) / 10;
T[3] = t % 10;
转化成四位数
int ntemp = T[0] * 1000 + T[1] * 100 + T[2] * 10 + T[3];
循环的方式存储每一位上的数字
for (int i = 3; i >= 0; --i) {
T[i] = g % 10;
g /= 10;
}
循环的方式转换成四位数
int ntemp = 0;
for (int i=0; i<=3; ++i){
ntemp = ntemp*10 + T[i];
}
通过对比发现循环的方式相较于数学取模的方式
不仅仅代码实现上更加简洁 通用性也更强(不仅只适用于4位数)
节点
struct Node {
int num;/*数字*/
int step;/*步数*/
};
线性素数筛
void Ols() {
/*线性素数筛*/
memset(su, true, sizeof(su));
su[0] = su[1] = false;
for (int i = 2; i < N; ++i) {
if (su[i]) au[++cnt] = i;
for (int j = 1; j <= cnt; ++j) {
if (i * au[j] >= N) break;
su[i * au[j]] = false;
if (i % au[j] == 0) break;
}
}
}
BFS过程
通过改变队头数字的每一位数字之后 再将新的节点增加到队尾
这样得到的结果一定符合题意是最少的次数
int BFS(int a, int b) {
if (a == b) return 0;/*如果一开始就相等*/
int T[4] = {0};
/*初始化*/
memset(vis, false, sizeof(vis));
queue<Node> que;
que.push({a, 0});/*起点入队*/
while (!que.empty()) {
Node fr = que.front();
int t = fr.num;/*获取队头数字*/
if (t == b) return fr.step;/*到达目标节点*/
/*四个位置上的数字*/
int g = t;
for (int i = 3; i >= 0; --i) {
T[i] = g % 10;
g /= 10;
}
for (int i = 0; i <= 3; ++i) {
int temp = T[i];/*保存*/
for (int j = 0; j <= 9; ++j) {
/*首位不能为零*/
if (i == 0 && j == 0) continue;
if (i == 3 && j % 2 == 0) continue;
T[i] = j;/*改变i位置上的数字*/
int ntemp = 0;
for (int i=0; i<=3; ++i){
ntemp = ntemp*10 + T[i];
}
/*如果出现过*/
if ( vis[ntemp]) continue;
vis[ntemp] = true;
/*如果改变之后不是素数*/
if (!su[ntemp]) continue;
/*新节点入队*/
que.push({ntemp, fr.step + 1});
}
T[i] = temp;/*恢复成改变之前的数字*/
}
que.pop();/*队头出队*/
}
}
完整代码
#include<iostream>
#include<queue>
#include<cstdlib>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 10010;
bool su[N];/*判断素数 TRUE为素数 FALSE为非素数*/
int au[N], cnt;/*素数表*/
bool vis[N];/*判重*/
struct Node {
int num;/*数字*/
int step;/*步数*/
};
int strToDigit(string str) {/*字符串转整数*/
int num = 0;
int len = str.size();
for (int i = len - 1; i >= 0; --i)
num = num * 10 + (str[i] - '0');
return num;
}
void Ols() {
/*线性素数筛*/
memset(su, true, sizeof(su));
su[0] = su[1] = false;
for (int i = 2; i < N; ++i) {
if (su[i]) au[++cnt] = i;
for (int j = 1; j <= cnt; ++j) {
if (i * au[j] >= N) break;
su[i * au[j]] = false;
if (i % au[j] == 0) break;
}
}
}
int BFS(int a, int b) {
if (a == b) return 0;/*如果一开始就相等*/
int T[4] = {0};
/*初始化*/
memset(vis, false, sizeof(vis));
queue<Node> que;
que.push({a, 0});/*起点入队*/
while (!que.empty()) {
Node fr = que.front();
int t = fr.num;/*获取队头数字*/
if (t == b) return fr.step;/*到达目标节点*/
/*四个位置上的数字*/
int g = t;
for (int i = 3; i >= 0; --i) {
T[i] = g % 10;
g /= 10;
}
for (int i = 0; i <= 3; ++i) {
int temp = T[i];/*保存*/
for (int j = 0; j <= 9; ++j) {
/*首位不能为零*/
if (i == 0 && j == 0) continue;
if (i == 3 && j % 2 == 0) continue;
T[i] = j;/*改变i位置上的数字*/
int ntemp = 0;
for (int i=0; i<=3; ++i){
ntemp = ntemp*10 + T[i];
}
/*如果出现过*/
if ( vis[ntemp]) continue;
vis[ntemp] = true;
/*如果改变之后不是素数*/
if (!su[ntemp]) continue;
/*新节点入队*/
que.push({ntemp, fr.step + 1});
}
T[i] = temp;/*恢复成改变之前的数字*/
}
que.pop();/*队头出队*/
}
}
int main() {
// freopen("C:\\Users\\24366\\Desktop\\out.txt", "w", stdout);
Ols();/*线性素数筛*/
int T;
cin >> T;
while (T--) {
int a, b;
cin >> a >> b;
cout << BFS(a, b) << endl;
}
return 0;
}