输入两个四位素数, 问从第一个数开始每次变换一个数字(变换之后得到的数必须也是素数,且不以0开头) 最少变换多少次能得到第二个数字,输出这个最少次数 (原题情景是安全部长叫首相换门牌号,换门牌号只能一个个数字换;然而首相要求门牌号必须始终是素数,包括变换途中;财政部长说每换一个数字要花一英镑,求问最少要花费多少英镑,其实就是问最少要多少步)
首先搜索方法用广搜(BFS), 即, 从原素数开始搜能从它变换一位数字得到的新素数, 每搜出一个新素数, 先把搜到的数放着,继续去搜下一个能从原素数变到的新素数, 变换一步就是一层,两步是两层, 等这一层的素数搜完了,发现没有产生最终目标素数,再从这一层逐一出发搜下一层... 这样就必然率先在最少层发现目标数,此时的层数就是最少步数
本代码BFS的具体实现是, 建一个二维数组bfs[][2]保存搜索结果,每行两个数字,表搜到的数字和变换次数,尽可能多行(无限行), 辅助变量有ibfs,总是表示bfs数组下一个空行的下标; bfslayerlength,总是表示最后搜索出的一层的结果个数; bu,表示步数(即层数,从0开始,第0层只有题目输入的第一个素数); 跟着还有iibfs,bfslayerlengthtemp分别用来维护上一次搜索的ibfs,bfslayerlength两个辅助变量的值,作为每一层搜索的循环起终点依据。
搜索一个数的下一步变化即, 分别枚举它个位、十位、百位、千位变换为其他数字的情况, 其中个位可以只考虑奇数, 千位只能考虑1到9, 然而枚举出的数还必须是素数才能存为搜索结果,这就需要素数判定算法
本代码是先用“埃拉托斯特尼筛法”即埃氏筛法来打出四位素数表(存为数组prime[]), 然后看待判的数是否在素数表中即可, 埃氏筛法的用法,具体步骤可以参考百度百科
此外,本题亲测还必须判重剪枝才能通过,否则搜索结果会有太多重复超出内存限制, 具体办法是,建一个与素数表等大的标记数组vis[](代码中用了整型来建,其实用bool型就可以而且能省很多空间), 每枚举出一个数除了经过以上剪枝项还要看这个素数在标记数组的对应位置是否有标记,有标记说明之前已经变出过这个素数,不列入搜索结果;否则,列入搜索结果并作标记。
最后,Impossible的判定:当发现再也枚举不出新的素数(不再产生新的搜索结果)时, 仍然没有出现过目标素数,就判为不可能
#include
using namespace std;
int main()
{
//埃氏筛打素数表
int* primeflag=new int[10000]();//此数组中某下标的元素为1时,这个下标就是素数
for (int i = 2; i <= 10000; i++)
primeflag[i] = 1;
int minflag = 0, maxflag = 10000;
while (maxflag >= minflag * minflag)
{
for (int i = minflag + 1; i <= maxflag; i++)
{
if (primeflag[i] == 1)
{
minflag = i;
break;
}
}
for (int i = minflag + 1; i <= maxflag; i++)
{
if (i%minflag == 0)
primeflag[i] = 0;
}
for (int i = 9999; i >= minflag; i--)
{
if (primeflag[i] == 1)
{
maxflag = i;
break;
}
}
}
int prime[1066] = { 0 };
int iprime = 0;
for (int i = 1000; i < 10000; i++)
{
if (primeflag[i] == 1)
{
prime[iprime] = i;
iprime++;
}
}
int vis[1066] = { 0 };
//执行至此得到四位素数表prime[],同时创建一个等大的标记数组vis用于避免后方的搜索产生重复素数(记得循环初始化标记数组)
int C;
cin >> C;
int bfs[2][100000];
int location;
int from, to;
int bu;
int ibfs, iibfs;
int bfslayerlength;
int isAnswerFound;
int isRepeatingStart;
for (C; C > 0; C--)
{
for (int i = 0; i < 2; i++)
for (int j = 0; j < 100000; j++)
bfs[i][j] = 0;
for (int i = 0; i < 1066; i++)
vis[i] = 0;
cin >> from >> to;
//BFS
bfs[0][0] = from;
location = 0;
bu = 1;
isAnswerFound = 0;
ibfs = 1;
iibfs = 0;
isRepeatingStart = 0;
bfslayerlength = 1;
while (isAnswerFound != 1)
{
isRepeatingStart = 0;
iibfs = ibfs;
for (int i = ibfs - 1; i >= ibfs - bfslayerlength; i--)//检查刚枚举完的情况是否出现了答案
{
if (bfs[0][i] == to)
{
cout << bfs[1][i] << endl;
isAnswerFound = 1;
break;
}
}
for (int k = ibfs - bfslayerlength; k < iibfs; k++)
{
if (isRepeatingStart != 1)
bfslayerlength = 0;
isRepeatingStart = 1;
from = bfs[0][k];
for (int i = 1; i <= 9; i += 2)//枚举改变个位数的情况
{
if (from / 10 * 10 + i != from)
location = from / 10 * 10 + i;
for (int j = 0; j < 1066; j++)
{
if (location == prime[j] && vis[j] == 0)
{
bfs[0][ibfs] = location;
bfs[1][ibfs] = bu;
ibfs++;
bfslayerlength++;
vis[j] = 1;
break;
}
}
}
for (int i = 0; i <= 9; i++)//枚举改变十位数的情况
{
if (from / 100 * 100 + from % 10 + i * 10 != from)
location = from / 100 * 100 + from % 10 + i * 10;
for (int j = 0; j < 1066; j++)
{
if (location == prime[j] && vis[j] == 0)
{
bfs[0][ibfs] = location;
bfs[1][ibfs] = bu;
ibfs++;
bfslayerlength++;
vis[j] = 1;
break;
}
}
}
for (int i = 0; i <= 9; i++)//枚举改变百位数的情况
{
if (from / 1000 * 1000 + from % 100 + i * 100 != from)
location = from / 1000 * 1000 + from % 100 + i * 100;
for (int j = 0; j < 1066; j++)
{
if (location == prime[j] && vis[j] == 0)
{
bfs[0][ibfs] = location;
bfs[1][ibfs] = bu;
ibfs++;
bfslayerlength++;
vis[j] = 1;
break;
}
}
}
for (int i = 1; i <= 9; i++)//枚举改变千位数的情况
{
if (from % 1000 + i * 1000 != from)
location = from % 1000 + i * 1000;
for (int j = 0; j < 1066; j++)
{
if (location == prime[j] && vis[j] == 0)
{
bfs[0][ibfs] = location;
bfs[1][ibfs] = bu;
ibfs++;
bfslayerlength++;
vis[j] = 1;
break;
}
}
}
}
bu++;
if (bfslayerlength == 0 && isAnswerFound != 1)//变换不再产生新素数时,判为不可能
{
cout << "Impossible" << endl;
break;
}
}
}
return 0;
}