暑期个人赛--第六场--E

时间限制 1000 ms  内存限制 65536 KB

题目描述

    学妹的手机马上就要没电了!

    手机网络是由蜂窝基站提供的,手机会自动搜寻最近的一个基站连接信号。每个基站的范围为一个正六边形。作为北邮的萌妹子当然知道切换基站是很费电的,所以要尽量不切换基站。学妹有一张地图在手中,想让你帮忙规划一下线路,让自己的手机尽可能少的切换基站。
    学妹的出发地和目的地均为平面直角坐标系上的点,为了方便计算,我们假设在坐标原点处有一个基站,基站范围的正六边形以基站为几何中心。
    六边形的边长为L,我们假设整个平面都有信号。小学妹很聪明,所以不会在基站间的边缘行走,也不会在三个基站区域相交的顶点停留,因为这样都会让手机付出非常大的耗电代价。
    学妹希望知道她最少需要穿越多少次边界才能够到达目的地。(六边形的方向为有两条边平行于y轴,有两个顶点分别朝北朝南)

 

输入格式

第一行为数据组数,整数T (T<=1000)
每组数据格式如下:
第一行 : 基站范围的正六边形边长,正实数 L ( 0.1 <= L <= 10.0 )
第二行 : 出发点坐标,两个实数Sx Sy  (-150000.0 <= Sx, Sy <= 150000.0)
第三行 : 目标点坐标,两个实数Dx Dy  (-150000.0 <= Dx, Dy <= 150000.0)

输出格式

每组数据输出一行,为一个整数,表示小胖最少需要穿越的基站范围边缘的次数。
数据保证起始点终点不会在六边形边缘上。

输入样例

2
2.0
0 0
6 -1
2.0
0 0
9 -1

输出样例

2
3

赛中提交:NULL

赛后AC:Y




题目大意:

给出正六边形的边长和起点终点坐标,保证原点处一定有一个正六边形,

要求求出从起点到达终点的最少穿越的边的个数。


思路:

如图采取y轴和x轴成π/3的建系方式,

有这样一个规律可以用:

(1)如果终点在起点的第一三象限,那么穿过边的个数就是起点到终点的曼哈顿距离

(2)如果终点在起点的第二四象限,那么穿过边的个数就取横纵坐标的较大者


接下来就是繁复复杂的坐标转换关系(其实只是有点曲折也不是很难理解不过让我想肯定是很难想出来的啦)

设边长为l,那么开始

(1)边长为l,那么l*sqrt(3)就是两个相邻六边形的中心点间的距离,(这个时候的单位距离就是中心点间距离)

然后我们将左边点采取除以l*sqrt(3)的处理方式(这是为了使加上之后的处理后,所有中心点都可以用整数表示)

(2)接下来将这个坐标系掰歪(咦?),具体操作是将纵坐标直接除以sin(π/3),而横坐标减去”原来纵坐标除以sqrt(3)“

(3)然后对以上处理完后的坐标点向下取整,这时候可以得到一个中心点,注意!但不是起/终点坐标对应的那个点。

(4)那么对应点要怎么求呢?虽然我们不能够确定这个点就是对应点,但是我们可以知道对应点一定在“这个点加上他周围点”组成的集合内

所以我们只需要枚举这些点就好了。将枚举得的点在还原回去(不像转换过来时有查错损失,转换回去的一定是精确的)再将其和原来的起/终点的距离进行对比

找出距离最小的那个点就好了



下面是ac代码


#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#define pi acos(-1)

using namespace std;
const double eps = 1e-6;
const double INF = 1e50;
struct p{
    int x,y;
};
p s,e;
double l;


double dis(double x1,double y1,double x2,double y2)
{
    return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}

p cal(double x,double y)
{
    p ans;
    int xn=floor(x-y/sqrt(3)+eps);
    int yn=floor(y/sin(pi/3)+eps);
    double maxd=INF;
    for(int i=xn-1;i<=xn+1;i+=1){
        for(int j=yn-1;j<=yn+1;j+=1){
            double a,b;
            b=(double)j*sin(pi/3);
            a=(double)i+(double)b/sqrt(3);
            if(dis(a,b,x,y)+eps<maxd){
                maxd=dis(a,b,x,y);
                ans.x=i;
                ans.y=j;
            }
        }
    }

    return ans;
}

int main()
{
    int  T;
    scanf("%d",&T);
    while(T--){
        scanf("%lf",&l);
        l*=sqrt(3);
        double tmpx,tmpy;
        scanf("%lf %lf",&tmpx,&tmpy);
        s=cal(tmpx/l,tmpy/l);
        scanf("%lf %lf",&tmpx,&tmpy);
        e=cal(tmpx/l,tmpy/l);

        //printf("%d %d ==\n",s.x,s.y);
        //printf("%d %d ==\n",e.x,e.y);
        if((s.x<e.x&&s.y<e.y)||(s.x>e.x&&s.y>e.y)){
            printf("%d\n",abs(e.x-s.x)+abs(e.y-s.y));
        }
        else{
            printf("%d\n",max(abs(e.x-s.x),abs(e.y-s.y)));
        }
    }

    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值