题目链接:POJ 3126
题意:
给出两个数a,b,且a,b均为四位素数,a<b。每次只改变a的一位数字,且改变后的数字仍是四位素数(也就是千位不能变为0),经过若干次操作使得最终从a变为b,问最少经历多少次操作?
思路:
先对四位正整数素数打表,然后用BFS搜索,每次从队列首去除一个元素,判断是否是目标数,否则将由这个数产生的符合条件的数字入队列(先判断是否是目标数)。开两个数组,一个用于记录当前数字是否访问过,一个用于记录到达当前数字的步骤数。
#include <iostream>
#include <queue>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <sstream>
using namespace std;
const int maxn = 10000;
int IsPrime[maxn],vis[maxn],cnt[maxn];
int n, a, b, ans;
int isprime(int n)
{//判断n是否为素数
for (int i = 2;i*i <= n;i++)
if (n%i == 0) return 0;
return 1;
}
void init()
{//对[1000,9999]的正整数进行素数打表
for (int i = 1000;i < maxn;i++)
IsPrime[i] = isprime(i);
}
int BFS(int first,int last)
{
memset(vis, 0, sizeof(vis));//vis[i]=0表示i尚未访问过
memset(cnt, 0, sizeof(cnt));//cnt[i]表示到达i经历的步骤数
queue<int> q;
int v, temp, vtemp, digit[4];
q.push(first);//将起始数入队列
vis[first] = 1;//起始数访问过了
while (!q.empty())//队列非空时
{
v = q.front();//取队首元素
q.pop();//队首元素出队列
if (v == last) return cnt[v];//队首元素==目标值,返回次数
digit[0] = v / 1000;//千位
digit[1] = v / 100 % 10;//百位
digit[2] = v / 10 % 10;//十位
digit[3] = v % 10;//个位
for (int i = 0;i < 4;i++)
{//对每个数需要改变个十百千每个位上的数字
temp = digit[i];
for (int j = 0;j < 10;j++)
{//每个位上的数字从0到9开始替换
if (temp != j)
{//数字不重合
digit[i] = j;//这样做方便了计算vtemp!
vtemp = digit[0] * 1000 + digit[1] * 100 + digit[2] * 10 + digit[3];
if (vtemp > 999 && !vis[vtemp] && IsPrime[vtemp])
{//千位不为0&&尚未访问过&&是素数
cnt[vtemp] = cnt[v] + 1;//到达vtemp的步骤数=到达v的步骤数+1
vis[vtemp] = 1;//标记访问过
q.push(vtemp);//入队列
}
if (vtemp == last) return cnt[vtemp];
}
}
digit[i] = temp;//别忘记替换回来!
}
}
return -1;
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
init();
while (~scanf("%d", &n))
{
while (n--)
{
scanf("%d%d", &a, &b);
ans = BFS(a,b);
if (ans == -1) printf("Impossible\n");
else cout << ans << endl;
}
}
return 0;
}