Time Limit: 3000/1000MS (Java/Others)
Memory Limit: 65535/65535KB (Java/Others)
方老师最近很喜欢素数,他想玩一个游戏:
现在有两个4位的素数n和m,你一次可以改变n的一位数字,并且改变过后的新数字必须也是个素数,并且也不能有前导0。请问使n变为m最少需要多少步。
例如
n=1033
m=8179
那么可行的变化是:
1033
1733
3733
3739
3779
8779
8179
Input
第一行有一个整数T(T≤100),代表测试数据的组数。
对于每组数据,每行有两个4位素数N,M(没有前导0)
Output
对于每一组数据,如果能够得到m,输出最少的步数,否则输出Impossible
Sample Input
3
1033 8179
1373 8017
1033 1033
Sample Output
6
7
0
题解:
我先打了个素数表,发现1000~9999的素数个数只有不到1500,那么我们只要判断任意两个素数之间能不能互相转换,如果能的话,那就在他们呢之间连一条边,很幸运,然后将在并查集中将这两个素数所在的集合合并。然后对于每一对n,m,只需要判断n,m是否在一个集合内,如果不在就直接输出-1,否则用n为起点跑一边单源最短路,然后n到m的最短距离就是最少变换次数。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#define LiangJiaJun main
#define INF 1999122700
using namespace std;
int T,ne,n,m;
int pri[1504],cnt,f[10004];
bool vis[10004];
int Find(int x){return f[x]==x?x:f[x]=Find(f[x]);}
int dis[10004],h[10004];
struct edge{int to,nt,w;}e[10004];
bool tip(int G, int L){
int dif=0;
while(G > 0 && L > 0){
if((G % 10) != (L % 10)) dif++;
G /= 10; L /= 10;
}
if(dif==1)return 1;
return 0;
}
void add(int u,int v){
e[++ne].to=v;
e[ne].nt=h[u];h[u]=ne;
}
int shai(){
vis[1]=1;
for(int i=2;i<=10000;i++)
for(int j=i+i;j<=10000;j+=i)vis[j]=1;
for(int i=1000;i<10000;i++)if(!vis[i])pri[++cnt]=i;
for(int i=1;i<=10000;i++)f[i]=i;
for(int i=1;i<=cnt;i++){
for(int j=i+1;j<=cnt;j++){
if(tip(pri[i],pri[j])){
int p=Find(pri[i]),q=Find(pri[j]);
if(p!=q)f[p]=q;
add(pri[i],pri[j]);
add(pri[j],pri[i]);
}
}
}
return 0;
}
queue<int>q;
int spfa(int s,int t){
memset(vis,0,sizeof(vis));
for(int i=1;i<=10000;i++)dis[i]=INF;
dis[s]=0;q.push(s);vis[s]=1;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=h[x];i;i=e[i].nt){
if( dis[e[i].to]>dis[x]+1){
dis[e[i].to]=dis[x]+1;
if(!vis[e[i].to]){
vis[e[i].to]=1;
q.push(e[i].to);
}
}
}
}
return dis[t];
}
int w33ha(){
scanf("%d%d",&n,&m);
if(Find(n)!=Find(m))return puts("-1"),0;
if(n==m)return puts("0"),0;
printf("%d\n",spfa(n,m));
return 0;
}
int LiangJiaJun(){
scanf("%d",&T);
shai();
while(T--)w33ha();
return 0;
}