WiFi 连接题解

Description

假设手机在连接 WiFi 时总是连接最近的路由器, 因此拿着手机移动时, 随着与每个路由器距离的变化会进行路由器(相同SSID)连接的转换.

畅春园宿舍楼的每一间宿舍都安装了一个路由器, 路由器安装的具体位置不固定, Alice 拿着手机在宿舍楼附近移动, 现在他想知道移动的过程中共进行了多少次路由器连接的转换.

为了简化问题, 现在假设 Alice 每次选择一个起点和一个终点, 移动的路线为直线.

Input

输入的第一行为两个正整数 n , m (n ∈ [3,50], m ∈[2,50]), n 表示 Alice 移动的可能起点或终点的坐标个数, m 表示宿舍楼安装的路由器个数.

随后 n 行, 每一行两个整数 x, y∈[0,1000], 表示起点或终点的坐标.

接下来 m 行,每一行两个整数 x, y∈[0,1000], 表示路由器位置的坐标.

接下来一行为一个整数 k, 表示 Alice 实验的次数.

后续 k 行, 每一行两个正整数 a, b∈[1,n], a 表示起点坐标的序号, b 表示终点坐标的序号. (序号指的是前面输入起点和终点坐标的顺序)

Output

对于 Alice 的每次实验, 输出单独的一行, 表示实验过程中路由器连接转换的次数.


定义点如下:

class Point{
public:
    double x, y;
    Point(double x = 0, double y = 0) : x(x), y(y) {}
};

由于所给的坐标都是整数,点与点之间最近距离为 1 ,所以我们可以选择一个合适的步长来模拟 Alice 从起点到终点的过程。

不妨取步长 stepX = 0.5,如果起点到终点的线段垂直于 x 轴,只改变 y 即可,否则 y 方向上的步长 stepY 可以由 x 的步长 stepX 与斜率相乘获得。

double stepX, stepY;
if (start.x == end.x){
    stepX = 0;
    stepY = 0.5;
}
else{
    double k = (end.y - start.y) / (end.x - start.x);
    stepX = 0.5;
    stepY = stepX * k;
}

定义动点 s ,从起点出发。

Point s = start;

只要动点未到达终点,每次求得距离动点当前位置最近的路由器 nextN,与上一步最近路由器 nearest 比较,若不同则说明切换了路由器,保存切换次数的 res 加一。

while (s.x <= end.x){
    int nextN = getNearestRouter(s);
    if (nextN != nearest){
        res++;
        nearest = nextN;
    }
    s.x += stepX;
    s.y += stepY;
}

其中 getNearestRouter(s) 返回距离点 s 最近的路由器编号。

int getNearestRouter(const Point &p){
    int nearest = 0; //初值取p和第一个路由器的距离
    double dist = getDist(p, router[nearest]);
    for (int i = 1; i < m; i++){
        double d = getDist(p, router[i]);
        if (d < dist){
            nearest = i;
            dist = d;
        }
    }
    return nearest;
}

由于默认动点坐标加上步长,所以注意调整起点位置,使得起点在终点左侧或正下方。不满足条件的话互换起点与终点即可。

if (points[start].x < points[end].x)
    cout << countConnections2(points[start], points[end]) << endl;  //左侧
else if (points[start].x == points[end].x){
    if (points[start].y < points[end].y)
        cout << countConnections2(points[start], points[end]) << endl;  //正下方
    else
        cout << countConnections2(points[end], points[start]) << endl;  //正上方
}
else
    cout << countConnections2(points[end], points[start]) << endl;  //右侧

总体代码如下:

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;

class Point{
public:
    double x, y;
    Point(double x = 0, double y = 0) : x(x), y(y) {}
};

int n, m;
vector<Point> points;
vector<Point> router;


double getDist(const Point &p1, const Point &p2){
    double x = p1.x - p2.x;
    double y = p1.y - p2.y;
    return sqrt(x * x + y * y);
}


int getNearestRouter(const Point &p){
    int nearest = 0;
    double dist = getDist(p, router[nearest]);
    for (int i = 1; i < m; i++){
        double d = getDist(p, router[i]);
        if (d < dist){
            nearest = i;
            dist = d;
        }
    }
    return nearest;
}

int countConnections2(Point &start, Point &end){
    Point s = start;
    double stepX, stepY;
    if (start.x == end.x){
        stepX = 0;
        stepY = 0.5;
    }
    else{
        double k = (end.y - start.y) / (end.x - start.x);
        stepX = 0.5;
        stepY = stepX * k;
    }
    int res = -1;
    int nearest = -1;  //没有序号为 -1 的路由器
    while (s.x <= end.x){
        int nextN = getNearestRouter(s);
        if (nextN != nearest){
            res++;
            nearest = nextN;
        }
        s.x += stepX;
        s.y += stepY;
    }
    return res;
}

int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i ++){
        Point temp;
        cin >> temp.x >> temp.y;
        points.push_back(temp);
    }
    for (int i = 0; i < m; i ++){
        Point temp;
        cin >> temp.x >> temp.y;
        router.push_back(temp);
    }
    int k;
    cin >> k;
    while (k --){
        int start, end;
        cin >> start >> end;
        start--;
        end--;
        
        if (points[start].x < points[end].x)
            cout << countConnections2(points[start], points[end]) << endl;  //左侧
        else if (points[start].x == points[end].x){
            if (points[start].y < points[end].y)
                cout << countConnections2(points[start], points[end]) << endl;  // 正下方
            else
                cout << countConnections2(points[end], points[start]) << endl;  //正上方
        }
        else
            cout << countConnections2(points[end], points[start]) << endl;  //右侧
    }
    return 0;
}

借鉴大佬思路实现的另一种方法是:

考虑到从起点到终点的过程中,发生路由器切换的点一定位于切换双方所连线段的垂直平分线上,此时该切换点距离两路由器距离相等。

我们可以先取得距离起点最近的路由器作为连接路由器,然后为该路由器和其他所以路由器作垂直平分线。注意若有多个路由器与起点距离相同且最近,取这些路由器中距离终点最近的那一个。

int nearest = 0;
double nearestDist = getDist(start, router[nearest]);
for (int i = 1; i < m; i ++){
    double d = getDist(start, router[i]);
    if (fabs(d - nearestDist) < 1e-5){  //距离相等
        double d1 = getDist(router[nearest], end);
        double d2 = getDist(router[i], end);
        if (d2 < d1){  //替换为离终点更近的
            nearest = i;
            nearestDist = d;
        }
    }
    else if (d < nearestDist){  //有更近的路由器
        nearest = i;
        nearestDist = d;
    }
}  //找到了最近的路由器编号

1. 若垂直平分线与路径线段未相交,那么一定没有切换点,未发生路由器切换

2. 若有 n (n >= 1)条垂直平分线与路径线段相交,取距离起点最近的交点作为这一次的切换点,其余交点无效。

记录切换次数的 res 加一,选中的交点作为新的起点,选中的垂直平分线对应的另一个路由器为新的连接路由器,循环上述过程,直到无法发生切换,即可得到结果。

整体代码如下:

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;

class Point{
public:
    double x, y;
    Point(double x = 0, double y = 0) : x(x), y(y) {}
};

int n, m;
vector<Point> points;
vector<Point> router;

class Line{  // ax + by = c
public:
    double a, b, c;
    Line(const Point &p1, const Point &p2, bool signal){
        if (signal){  //垂直平分线
            a = 2 * (p2.x - p1.x);
            b = 2 * (p2.y - p1.y);
            c = p2.x * p2.x + p2.y * p2.y - p1.x *p1.x - p1.y * p1.y;
        }
        else{  //两点连线
            a = p1.y - p2.y;
            b = p2.x - p1.x;
            c = p2.x * p1.y - p1.x * p2.y;
        }
    }
};

Point crossPoint(const Line &l1, const Line &l2){ //克拉默法则求交点
    Point cross;
    if (l1.a * l2.b == l2.a * l1.b){  //平行,(-1, -1)一定不满足between()
        cross.x = -1;
        cross.y = -1;
    }
    else{
        cross.x = (l1.c * l2.b - l2.c * l1.b) / (l1.a * l2.b - l2.a * l1.b);
        cross.y = (l1.a * l2.c - l2.a * l1.c) / (l1.a * l2.b - l2.a * l1.b);
    }
    return cross;
}

bool between(const Point &p, const Point &start, const Point &end){
    if (start.x == end.x) return (p.y - start.y) * (p.y - end.y) < 0;  //垂直线
    else return (p.x - start.x) * (p.x - end.x) < 0;
}

double getDist(const Point &p1, const Point &p2){  //求两点间距离
    double x = p1.x - p2.x;
    double y = p1.y - p2.y;
    return sqrt(x * x + y * y);
}

int countConnections(Point &start, Point &end){
    Line s2e(start, end, false);
    int nearest = 0;
    double nearestDist = getDist(start, router[nearest]);
    for (int i = 1; i < m; i ++){
        double d = getDist(start, router[i]);
        if (fabs(d - nearestDist) < 1e-5){  //距离相等
            double d1 = getDist(router[nearest], end);
            double d2 = getDist(router[i], end);
            if (d2 < d1){  //替换为离终点更近的
                nearest = i;
                nearestDist = d;
            }
        }
        else if (d < nearestDist){  //有更近的路由器
            nearest = i;
            nearestDist = d;
        }
    }  //找到了最近的路由器编号
    int res = 0;
    Point C = start;
    while (true){
        Point nextC = end;
        int nextID = -1;
        for (int i = 0; i < m; i ++){
            if (nearest == i) continue;
            Line routerLine(router[nearest], router[i], true);
            Point cross = crossPoint(routerLine, s2e);
            if (between(cross, C, nextC)){ //为了找最近,将区间右端点前移
                nextC = cross;
                nextID = i;
            }
        }
        //无交点
        if (nextID == -1) break;
        else{
            nearest = nextID;
            C = nextC;
            res ++;
        }
    }
    return res;
}


int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i ++){
        Point temp;
        cin >> temp.x >> temp.y;
        points.push_back(temp);
    }
    for (int i = 0; i < m; i ++){
        Point temp;
        cin >> temp.x >> temp.y;
        router.push_back(temp);
    }
    int k;
    cin >> k;
    while (k --){
        int start, end;
        cin >> start >> end;
        start--;
        end--;
        cout << countConnections(points[start], points[end]) << endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值