POJ 3126 Prime Path 解题报告(BFS & 双向BFS)

来源:互联网 发布:java合成多张图片 编辑:程序博客网 时间:2024/05/21 07:53

    题目大意:给定一个4位素数,一个目标4位素数。每次变换一位,保证变换后依然是素数,求变换到目标素数的最小步数。

    解题报告:直接用最短路。

    枚举1000-10000所有素数,如果素数A交换一位可以得到素数B,则在AB间加入一条长度为1的双向边。

    则题中所求的便是从起点到终点的最短路。使用Dijkstra或SPFA皆可。

    当然,纯粹的BFS也是可以的。

    用Dijkstra算法A了题目之后,看了一下Discuss,发现了一个新名词,双向BFS。

    即从起点和终点同时进行BFS,相遇则求得最短路。

    借鉴了思想,自己动手实现了代码。原本以为双向比单向快一倍而已,其实远远不止。

    笔者用30W数据分别测试了单向和双向。环境为CodeBlock+MinGW4.7,Debug,双向时间为8.618s,而单向为惊人的139.989s!

    简单思考了一下,也还是合理的。单向每次的增长是指数级的,而双向的指数只有单向的一半,优化程度相当高。

    好了,贴代码~首先是双向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);    }}


    另一版本代码:

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;bool prime[10000];void calPrime(){    for(int i=2;i<10000;i++) if(!prime[i])        for(int j=i+i;j<10000;j+=i) prime[j]=true;    for(int i=2;i<1000;i++) prime[i]=true;}int ten[] = {1, 10, 100, 1000, 10000};bool vis[10000];int stack[10000][2];int top[2];void work(){    int a, b;    scanf("%d%d", &a, &b);    int now=1,pre=0;    memset(vis, 0, sizeof(vis));    top[now]=0;    stack[top[now]++][now]=a;    vis[a]=true;    for(int t=0;;t++)    {        swap(now, pre);        top[now]=0;        if(top[pre]==0) return ;        for(int j=0;j<top[pre];j++)        {            int num=stack[j][pre];            if(num==b)            {                printf("%d\n", t);                return;            }            for(int i=0;i<4;i++)            {                int newNum = num-(num%ten[i+1]-num%ten[i]);                for(int k=0;k<10;k++,newNum+=ten[i])                    if(!prime[newNum] && !vis[newNum])                        stack[top[now]++][now]=newNum, vis[newNum]=true;            }        }    }}int main(){    calPrime();    int T;    scanf("%d", &T);    while(T--)        work();}