POJ 3126 Prime Path 解題報告(BFS & 雙向BFS)

    題目大意:給定一個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

相關文章
相關標籤/搜索