题目大意:给定两个四位素数a b,要求把a变换到b,变换的过程要保证 每次变换出来的数都是一个 四位素数,而且当前这步的变换所得的素数 与 前一步得到的素数 只能有一个位不同,而且每步得到的素数都不能重复。
题目链接:点击打开链接
分析:分析可知这题肯定是用搜索,每次改变某位,每位有0-9(首位无0)10种变法,一共40个方向,可是到底DFS还是BFS还是二分呢?可只二分对这题显然是不太好写的,DFS的话复杂度为O(40^n)估计几年也出不了结果来,,,然后BFS的话O(40*n)肯定是可以过的。BFS是很好写,麻烦的就在数字的转变和素数的判断。这里素数的判断由于是多组数据我们可以用埃式筛选打一个表(怎么实现可以直接看代码,自己思考下就行了,这里朴素的方法也是可以过的)。对于改变一个数字的某一位我们可以用到sprintf和sscanf这2个黑科技来简单的实现,只要先把初始数字v用sprintf打印到字符串里,然后利用字符串的随机访问来改变其中某一位就行了,接下来再用sscanf再输出到v中即可。
附上代码:
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
#define Max 10000+5
bool vis[Max], is_prime[Max];
int d[Max];
int T, a, b;
void get_prime() //打素数表
{
memset(is_prime, true, sizeof is_prime);
for (int i = 2; i*i < Max; i++)
if (is_prime[i])
for (int j = 2 * i; j < Max; j += i)
is_prime[j] = false;
return;
}
int judge(int v,int i,int j)
{
if (!is_prime[v]) return 0; //不是素数返回0
if (i == 1 && j == 0) return 0; //首位为0返回0
char s[6] = "\0";
sprintf(s, "%d", v);
s[i - 1] = j + '0'; //将第i位改成j
sscanf(s, "%d", &v);
if (!vis[v]) return v; //返回改变后的值
return 0;
}
int bfs()
{
queue<int> q;
q.push(a);
vis[a] = 1;
while (!q.empty())
{
int v = q.front();
q.pop();
for (int i = 1; i <= 4; i++) //枚举40个入口,分别为第i位改成j
for (int j = 0; j <= 9; j++)
{
int t = judge(v, i, j);
if (t)
{
vis[t] = 1;
d[t] = d[v] + 1;
q.push(t);
if (t == b) return d[t];
}
}
}
return -1;
}
int main()
{
scanf("%d", &T);
get_prime();
while (T--)
{
memset(vis, false, sizeof vis);
memset(d, false, sizeof d);
scanf("%d%d", &a, &b);
if (a == b) printf("0\n");
else
{
int ans = bfs();
if (ans == -1) printf("Impossible\n");
else printf("%d\n", ans);
}
}
return 0;
}