題目大意:給定一個4位素數,一個目標4位素數。每次變換一位,保證變換後依然是素數,求變換到目標素數的最小步數。node
解題報告:直接用最短路。算法
枚舉1000-10000全部素數,若是素數A交換一位能夠獲得素數B,則在AB間加入一條長度爲1的雙向邊。測試
則題中所求的即是從起點到終點的最短路。使用Dijkstra或SPFA皆可。優化
固然,純粹的BFS也是能夠的。spa
用Dijkstra算法A了題目以後,看了一下Discuss,發現了一個新名詞,雙向BFS。code
即從起點和終點同時進行BFS,相遇則求得最短路。blog
借鑑了思想,本身動手實現了代碼。本來覺得雙向比單向快一倍而已,其實遠遠不止。string
筆者用30W數據分別測試了單向和雙向。環境爲CodeBlock+MinGW4.7,Debug,雙向時間爲8.618s,而單向爲驚人的139.989s!it
簡單思考了一下,也仍是合理的。單向每次的增加是指數級的,而雙向的指數只有單向的一半,優化程度至關高。io
好了,貼代碼~首先是雙向BFS的Dijkstra:
#include <cstdio> #include <cstring> #include <queue> using namespace std; const int maxn=10010; int prime[maxn]; const int maxV=1100; int first[maxV],vv[maxV*maxV],nxt[maxV*maxV]; int num[maxV]; bool vis[2][maxV]; int index; bool check(int a,int b) { int k=a-b; if(k%1000==0) return true; if(k<1000 && k%100==0 && a/1000==b/1000) return true; if(k<100 && k%10==0 && a/100==b/100) return true; if(a/10==b/10) return true; return false; } void calPrime() { for(int i=2;i<maxn;i++) if(!prime[i]) { for(int j=2*i;j<maxn;j+=i) prime[j]=true; if(i>=1000 && i<10000) { num[++index]=i; prime[i]=index; } } int e=2; memset(first,0,sizeof(first)); for(int i=1;i<=index;i++) for(int j=i+1;j<=index;j++) if(check(num[j],num[i])) { nxt[e]=first[i],vv[e]=j,first[i]=e++; nxt[e]=first[j],vv[e]=i,first[j]=e++; } } struct Node { int node; int level; bool operator<(const Node& cmp) const { return level>cmp.level; } } p,q; int Dijkstra(int sta,int end) { if(sta==end) return 0; memset(vis,0,sizeof(vis)); sta=prime[sta]; end=prime[end]; priority_queue<Node> pq[2]; p.node=sta; p.level=0; vis[0][p.node]=true; pq[0].push(p); p.node=end; p.level=0; vis[1][p.node]=true; pq[1].push(p); for(int i=0; !pq[0].empty() && !pq[1].empty() ;i++) { int sel=0; if(pq[0].size()>pq[1].size()) sel++; int level=pq[sel].top().level; while(!pq[sel].empty()) { p=pq[sel].top(); if(p.level!=level) //先判斷,不然會pop掉丟失狀況 break; pq[sel].pop(); for(int e=first[p.node];e;e=nxt[e]) { if(vis[1-sel][vv[e]]) return i+1; if(!vis[sel][vv[e]]) { q.level=p.level+1; q.node=vv[e]; vis[sel][vv[e]]=true; pq[sel].push(q); } } } } return -1; } int main() { calPrime(); int T; scanf("%d",&T); while(T--) { int sta,end; scanf("%d%d",&sta,&end); int ans=Dijkstra(sta,end); if(ans==-1) printf("Impossible\n"); else printf("%d\n",ans); } }
而後是單向的BFS+Dijkstra:
#include <cstdio> #include <cstring> #include <queue> using namespace std; const int maxn=10010; int prime[maxn]; const int maxV=1100; int first[maxV],vv[maxV*maxV],nxt[maxV*maxV]; int num[maxV]; bool vis[maxV]; int index; int count; bool check(int a,int b) { int k=a-b; if(k%1000==0) return true; if(k<1000 && k%100==0 && a/1000==b/1000) return true; if(k<100 && k%10==0 && a/100==b/100) return true; if(a/10==b/10) return true; return false; } void calPrime() { for(int i=2;i<maxn;i++) if(!prime[i]) { for(int j=2*i;j<maxn;j+=i) prime[j]=true; if(i>=1000 && i<10000) { num[++index]=i; prime[i]=index; } } int e=2; memset(first,0,sizeof(first)); for(int i=1;i<=index;i++) for(int j=i+1;j<=index;j++) if(check(num[j],num[i])) { nxt[e]=first[i],vv[e]=j,first[i]=e++; nxt[e]=first[j],vv[e]=i,first[j]=e++; } } struct Node { int k; int w; bool operator<(const Node& cmp) const { return w>cmp.w; } } p,q; int Dijkstra(int sta,int end) { memset(vis,0,sizeof(vis)); end=prime[end]; p.k=prime[sta]; p.w=0; vis[p.k]=true; priority_queue<Node> pq; pq.push(p); while(!pq.empty()) { p=pq.top(); pq.pop(); if(p.k==end) return p.w; for(int e=first[p.k];e;e=nxt[e]) if(!vis[vv[e]]) { q.k=vv[e]; q.w=p.w+1; vis[q.k]=true; pq.push(q); } } return -1; } int main() { calPrime(); int T; scanf("%d",&T); while(T--) { int sta,end; scanf("%d%d",&sta,&end); int ans=Dijkstra(sta,end); if(ans==-1) printf("Impossible\n"); else printf("%d\n",ans); } }
測試數據我放在了百度雲,有興趣能夠下載下來試一下:http://pan.baidu.com/share/link?shareid=4217669741&uk=2804348991