Description The ministers of the cabinet were quite upset by the message from the Chief of Security stating that they would all have to change the four-digit room numbers on their offices.
— It is a matter of security to change such things every now and then, to keep the enemy in the dark.
— But look, I have chosen my number 1033 for good reasons. I am the Prime minister, you know!
— I know, so therefore your new number 8179 is also a prime. You will just have to paste four new digits over the four old ones on your office door.
— No, it’s not that simple. Suppose that I change the first digit to an 8, then the number will read 8033 which is not a prime!
— I see, being the prime minister you cannot stand having a non-prime number on your door even for a few seconds.
— Correct! So I must invent a scheme for going from 1033 to 8179 by a path of prime numbers where only one digit is changed from one prime to the next prime.
Now, the minister of finance, who had been eavesdropping, intervened.
— No unnecessary expenditure, please! I happen to know that the price of a digit is one pound.
— Hmm, in that case I need a computer program to minimize the cost. You don't know some very cheap software gurus, do you?
— In fact, I do. You see, there is this programming contest going on... Help the prime minister to find the cheapest prime path between any two given four-digit primes! The first digit must be nonzero, of course. Here is a solution in the case above.
1033
1733
3733
3739
3779
8779
8179 The cost of this solution is 6 pounds. Note that the digit 1 which got pasted over in step 2 can not be reused in the last step – a new 1 must be purchased.
Input One line with a positive number: the number of test cases (at most 100). Then for each test case, one line with two numbers separated by a blank. Both numbers are four-digit primes (without leading zeros).
Output One line for each case, either with a number stating the minimal cost or containing the word Impossible.
Sample Input
31033 81791373 80171033 1033
Sample Output
670
題意:由第一個數每次改變一位,改成第二個數需要幾步(兩個數都是素數)
本題其實就是在找初始素數至目標素數的最短路徑,應該使用優先搜尋,本題其實沒有必要用隊列,因為隊列自動排序的特點根本就是沒有用的,用數組就夠了,但是可能會增加變數來控制當前入隊的情況,有時間用數組做一下........
本題注意
(1)本題是圖的性質,而不是樹
(2)由於資料比較龐大,應該首先考慮離線求素數的方式(篩法)
(3)使用queue時,要注意queue的特點,是在遍曆每一個當前值得時候,都將值壓入當前的分支內,而不是單獨的,所以遍曆的時候注意遍曆的順序(見後面插圖)
是一個節點一個節點的訪問(包括當前節點的孩子)
(4)本人認為本題的精髓,也是作為與樹區別最大的特點就是加了一個visit[]數組,作為標記是否已經訪問過的標記,這樣的話,一方面不會造成程式的死迴圈,另一方面也會滿足本題最小值的條件,因為在前面已經訪問過的話,肯定比當前的小
#include <iostream>#include <stdio.h>#include <queue>#include <math.h>#include <string.h>#define N 10010using namespace std;bool isPrm[N],visit[N];//isPrm[N]是用來記錄是否為素數的,visit[]是用來記錄是否已經遍曆過int path_c[N]; //記錄路徑的長度void getPrm(){//求素數法(快速篩法) int s,e=sqrt((double)N)+1;//sqrt是對於double數開平方 memset(isPrm,1,sizeof(isPrm)); isPrm[0]=isPrm[1]=0; for(int i=4;i<N;i+=2)isPrm[i]=0; for(int i=3;i<e;i+=2) if(isPrm[i]) for(int s=i*2,j=i*i;j<N;j+=s) isPrm[j]=0;}int main(){ int n,m,t; getPrm(); scanf("%d",&t); while(t--){ int mark=0; queue<int> q; cin>>n>>m; memset(visit,false,sizeof(visit)); memset(path_c,0,sizeof(path_c)); q.push(n);//第一個資料壓入隊列中 visit[n]=true;//該點遍曆過 while(!q.empty()){if(mark==1) break; int temp=q.front();//取出第一個,出隊元素q.pop();//彈出 if(temp==m) break;//直至等於m,停止 for(int i=1;i<=1000;i=i*10) {//4個位置,個十百千位上一次為0-9的BFS的遍曆,i決定著當前是分離哪位 if(mark==1) break; int d=temp/i%10,w=temp-d*i; //將當前位的數字分離出來,d是被分離出來位上的數 //去掉該位以後的數 for(int j=0;j<10;j++){ //每一位上0-9的BFS的遍曆,j是要替換當前位的數 if(i==1000&&j==0) continue;//最高位不為0 if(j!=d){ //不是原來的那個位上的數 int now=j*i+w; //產生一個新的數 if(isPrm[now]&&!visit[now]){//isPrm[now]如果是素數,且!visit[now]沒有被訪問過 q.push(now);//入隊 path_c[now]=path_c[temp]+1;//在原來的基礎上加1,標誌走過的路徑長度if(now==m){mark=1;break;}visit[now]=true;//標誌訪問過 } } } } } printf("%d\n",path_c[m]); } return 0;}
注意兩種方式的對比,下面的這種情況本人感覺不是很好理解,因為在找出要找的數值之後還會繼續尋找直到訪問該節點的才會停止,其實是早就遇到當前的值了,沒有必要還在遇到要找的值之後還繼續,直到訪問到才結束,還是當面的好一點,感覺自己寫的比較簡潔省時
int main(){ int n,m,t; getPrm(); scanf("%d",&t); while(t--) { int mark=0; queue <int> q; cin>>n>>m; memset(visit+900 ,false,sizeof(visit)); memset(path_c+900,0,sizeof(path_c)); q.push(n); visit[n]=true; while(!q.empty()) { if(mark==1) break; int temp=q.front(); q.pop(); if(temp==m) break; for(int i=1;i<=1000;i=i*10) { if(mark==1) break; int d=temp; d=d/i; d=d%10; int w=temp-d*i; for(int j=0;j<10;j++) { if(i==1000&&j==0) continue; if(j!=d) { int now=j*i+w; if(isPrm[now]&&!visit[now]) { q.push(now); path_c[now]=path_c[temp]+1; if(now==m){mark=1;break;} visit[now]=true; } } } } } printf("%d\n",path_c[m]); } return 0;}
看了之前寫的代碼都有點好笑,怎麼還能暴力1000然後分離出來呢,也是醉,重寫了個更新下:
#include<cstdio>#include<cstring>#include<cmath>#include<cstdlib>#include<iostream>#include<algorithm>#include<vector>#include<map>#include<queue>#include<stack>#include<string>#include<map>#include<set>#include<ctime>#define eps 1e-6#define MAX 10005#define INF 0x3f3f3f3f#define LL long long#define pii pair<int,int>#define rd(x) scanf("%d",&x)#define rd2(x,y) scanf("%d%d",&x,&y)#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)///map<int,int>mmap;///map<int,int >::iterator it;using namespace std;bool isprm[MAX];bool vis[MAX];bool getprm(){ isprm[0]=isprm[1]=0; for(int i=2;i<MAX;i++) if(isprm[i]) for(int j=i*i;j<MAX;j+=i) isprm[j]=0;/* for(int i=0;i<MAX;i++) if(isprm[i]) cout<<i<<' ';*/}struct Str{ int num; int step; Str(){} Str(int num,int step){ this->num = num,this->step = step; }};int pow(int time){ int num=1; for(int i=0;i<time;i++) num*=10; return num;}int main(){ memset(isprm,1,sizeof(isprm)); getprm(); int T,start,eend; rd(T); while(T--){ memset(vis,0,sizeof(vis)); int res=INF; scanf("%d%d",&start,&eend); if(start==eend){ printf("0\n"); continue; } queue<Str>que; que.push(Str(start,0)); vis[start]; while(!que.empty()&&res==INF){ Str tmp = que.front(); que.pop(); for(int i=3;i>=0&&res==INF;i--){ int j=0; if(i==3) j=1; int bit = tmp.num/(int)pow(i)%10; for( ; j<=9;j++){ if(j==bit) continue; int newNum = tmp.num-(bit-j)*pow(i); if(isprm[newNum]){ if(newNum == eend){ res=tmp.step+1; break; } if(!vis[newNum]){ que.push(Str(newNum,tmp.step+1)); vis[newNum]=1; } } } } } if(res==INF) printf("Impossible\n"); else printf("%d\n",res); } return 0;}