POJ 2253 Frogger(并查集+二分)

POJ 2253 Frogger(并查集+二分)

http://poj.org/problem?id=2253

题意:

        给你N个石头的坐标(x,y),现在青蛙要从第一个石头跳到第二个石头上去(青蛙可以通过跳跃到其他石头而间接到达第2个石头上),但是青蛙每次最大的跳跃距离有限制。所以现在问你青蛙每次跳跃最少需要跳多长距离 才能 从第一个石头跳到第二个石头?

分析:

       网上说这题有最短路径解的,生成树解的.本来我是冲着最短路径解法来的,但是看了一遍我觉得这题用二分+并查集(判断1点与2点是否连通)才是比较直观的方法吧.

       首先我们二分出mid值表示青蛙每次能跳跃的最大距离,然后我们遍历所有的边,只要当前边的长度<=mid,就合并当前边连接的两个点所属的连通分量(因为青蛙如果在其中一点上,那么必定能调到另外一点所属分量的所有点上)。最后判断1号点与2号点是否在同一个连通分量,即可知道单次mid的跳跃距离是否足够。

AC代码(新):

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=200+5;

int n;
int fa[maxn];
int findset(int x)
{
    return fa[x]==-1? x:fa[x]=findset(fa[x]);
}
int bind(int u,int v)
{
    int fu=findset(u);
    int fv=findset(v);
    if(fu != fv)
    {
        fa[fu]=fv;
        return 1;
    }
    return 0;
}

struct Point
{
    double x,y;
}p[maxn];
double dist[maxn][maxn];

bool ok(double mid)
{
    memset(fa,-1,sizeof(fa));

    for(int i=0;i<n;i++)
    for(int j=i+1;j<n;j++)
    if(dist[i][j]<=mid)
        bind(i,j);

    return findset(0)==findset(1);
}

int main()
{
    int kase=0;
    while(scanf("%d",&n)==1 && n)
    {
        for(int i=0;i<n;i++)
            scanf("%lf%lf",&p[i].x,&p[i].y);

        for(int i=0;i<n;i++)
        for(int j=i;j<n;j++)
            dist[i][j]=dist[j][i]=sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y));

        double L=0,R=2000;
        while(R-L>1e-5)
        {
            double mid = (R+L)/2;
            if(ok(mid)) R=mid;
            else L=mid;
        }

        printf("Scenario #%d\nFrog Distance = %.3lf\n\n",++kase,L);
    }
    return 0;
}


AC代码:

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn=200+10;
int n;
double x[maxn],y[maxn];
double d[maxn][maxn];
int fa[maxn];
int find(int i)
{
    if(fa[i]==-1) return i;
    return fa[i] = find(fa[i]);
}
void bind(int i,int j)
{
    i=find(i);
    j=find(j);
    if(i!=j) fa[i]=j;
}
bool ok(double mid)
{
    memset(fa,-1,sizeof(fa));

    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)if(d[i][j]<mid)
            bind(i,j);
    }

    return find(1)==find(2);
}
int main()
{
    int kase=0;
    while(scanf("%d",&n)==1&&n)
    {
        for(int i=1;i<=n;i++)
            scanf("%lf%lf",&x[i],&y[i]);
        memset(d,0,sizeof(d));
        for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            d[i][j]=d[j][i]= sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
        double L=0,R=d[1][2];
        while(R-L>1e-7)     //这里是1e-7 不是1e7
        {
            double mid = (R+L)/2;
            if(ok(mid)) R=mid;
            else L=mid;
        }
        printf("Scenario #%d\nFrog Distance = %.3lf\n\n",++kase,R);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值