POJ 2253 Frogger(最小生成树)

    青蛙跳跃,题意大概是:青蛙从起点到终点进行一次或多次的跳跃,多次跳跃中肯定有最大的跳跃距离。求在所有的跳跃中,最小的最大跳跃距离SF-_-(不理解?看题目吧)。

    可以用最小生成树完成。以起点为根,生成一棵最小生成树,直到树里包含了终点。

    或者这么说吧,类似于Kruskal算法,我们每次选取不成环的最小边,直到这棵树选取了通往终点的最小边,那么最后选择的这条边必然是在树中最大的一条边,而且在其余的边中是最小的。你不会找到比这条边小的最大距离,因为比它小的最小距离都在树里了,而未选取该边前树中不包含终点,即比该边小的所有边无法到达终点。即改边满足的两个条件,最小,而且是起点到终点的最大距离(PS:挺绕的……)。

    既然有思路了,可以直接写代码了。先是上面的Kruskal算法(16MS):

#include <cstdio>

#include <cstring>

#include <cmath>

#include <algorithm>

using namespace std;



int root[202];

int x[202],y[202];



int find(int x)

{

    return root[x]?root[x]=find(root[x]):x;

}



bool union_set(int a,int b)

{

    a=find(a);

    b=find(b);

    if(a==b)

        return false;

    root[b]=a;

    return true;

}



struct Edge

{

    int x,y,dis;

    bool operator<(const Edge& cmp) const

    {

        return dis<cmp.dis;

    }

} edge[40000];



int main()

{

//  freopen("in.txt","r",stdin);

    int n,cas=1;

    while(~scanf("%d",&n) && n)

    {

        memset(root,0,sizeof(root));

        for(int i=1;i<=n;i++)

            scanf("%d%d",x+i,y+i);

        int index=0;

        for(int i=1;i<=n;i++)

        {

            for(int j=i+1;j<=n;j++)

            {

                edge[index].x=i;

                edge[index].y=j;

                edge[index++].dis=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);

            }

        }

        sort(edge,edge+index);



        int maxE=0;

        for(int i=0;i<index;i++)

        {

            if(union_set(edge[i].x,edge[i].y))

            {

                if(find(2)==find(1))

                {

                    maxE=edge[i].dis;

                    break;

                }

            }

        }

        printf("Scenario #%d\n",cas++);

        printf("Frog Distance = %.3f\n\n",sqrt((double)maxE));

    }

}

    使用并查集的Kruskal算法明显要好写一点。但是这题每两点之间必然有一条边,是一个稠密图,Prim算法更适合一些。下面是Prim算法的代码:

#include <cstdio>

#include <cstring>

#include <cmath>

#include <algorithm>

#include <queue>

using namespace std;



struct Node

{

    int k,w;

    bool operator<(const Node& cmp) const

    {

        return w>cmp.w;

    }

} p,q;



bool vis[202];

int x[202],y[202];

int first[202],vv[40001],ww[40001],nxt[40001];





int main()

{

//  freopen("in.txt","r",stdin);

    int n,cas=1;

    while(~scanf("%d",&n) && n)

    {

        priority_queue <Node> pq;

        memset(vis,0,sizeof(vis));

        memset(first,0,sizeof(first));



        for(int i=1;i<=n;i++)

            scanf("%d%d",x+i,y+i);



        int e=2;

        for(int i=1;i<=n;i++)

        {

            for(int j=i+1;j<=n;j++)

            {

                nxt[e]=first[i],vv[e]=j;

                ww[e+1]=ww[e]=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);

                first[i]=e++;

                nxt[e]=first[j],vv[e]=i;

                first[j]=e++;

            }

        }



        p.k=1;

        p.w=0;

        pq.push(p);



        int maxE=0;

        while(!pq.empty())

        {

            p=pq.top();

            pq.pop();

            maxE=max(maxE,p.w);

            if(p.k==2)

                break;

            if(vis[p.k])

                continue;

            vis[p.k]=true;



            for(int e=first[p.k];e;e=nxt[e]) if(!vis[vv[e]])

            {

                q.k=vv[e];

                q.w=ww[e];

                pq.push(q);

            }

        }



        printf("Scenario #%d\n",cas++);

        printf("Frog Distance = %.3f\n\n",sqrt((double)maxE));

    }

}

    需要注意的是Kruskal算法最终的判定是起点和终点是否同一集合,如果是,最大距离就是最后一条边的距离。而prim算法的最大边需要实时更新,因为先选的边可能大于后来选择的边。搞笑的是Prim算法也是16MS,不知道是不是我写的效率有问题SF0_0。夜深人静的时候在去跑跑吧……

你可能感兴趣的:(最小生成树)