UVA511 Do You Know the Way to San Jose? 题解

题目链接:UVA511 Do You Know the Way to San Jose?

题目描述:

互联网现在提供了多种交互式地图设施,用户既可以查看大范围地理区域的概览地图,也可以“放大”到某条具体街道,有时甚至能查看某栋具体建筑所在的更详细地图。例如,圣何塞市中心可能同时出现在加利福尼亚州地图、圣克拉拉县地图以及一张详细街道地图中。

假设你拥有大量矩形地图,并希望设计一个浏览设施,用来处理一系列针对不同地图细节级别地点的地图请求。地点使用地点名称表示。每个地点名称都有唯一的一对实数坐标 \((x,y)\)。每张地图都是唯一的,并带有用于标识的地图名称,且由两对实数坐标 \((x_1,y_1)\) 和 \((x_2,y_2)\) 定义,这两对坐标表示地图的两个对角点。所有地图的边都平行于标准笛卡尔 \(x\) 轴和 \(y\) 轴。地图和地点可以同名。地图的高宽比定义为其高度与宽度之比,其中宽度沿 \(x\) 方向度量,高度沿 \(y\) 方向度量。

一张地图的细节级别可以用该地图所表示的矩形面积来近似;也就是说,假设覆盖面积较小的地图包含更详细的信息。地图之间可以相互重叠。如果一个地点 \((x,y)\) 位于两张或更多面积相等的地图中,则在该细节级别下,优先选择地点离地图中心最近的那张地图。如果该地点到两张同面积重叠地图中心的距离相等,则优先选择高宽比最接近浏览器窗口高宽比 \(0.75\) 的地图。如果这样仍不能打破平局,则优先选择地点离地图右下角最远的那张地图(该启发式规则旨在尽量减少用户浏览器窗口中的滚动需求)。最后,如果仍然并列,则优先选择其 \(x\) 坐标最小的地图。

给定地点可用的最大细节级别,是包含该地点的不同面积地图数量的最大值。显然,不同地点可以有不同的最大细节级别。该地点在细节级别 \(i\) 下的地图,是在包含该地点的不同面积地图的一个最大集合中,面积第 \(i\) 大的地图。因此,该地点在细节级别 \(1\) 下的地图,将是包含该地点的最不详细(面积最大)的地图;而最大细节级别下的地图,将是最详细(面积最小)的地图。

输入格式:

输入文件由一组地图、地点和请求组成,形式如下:

  • 单独一行、全部大写的单词 MAPS 引入一个包含一张或多张地图的集合。在该集合标题之后,每张地图由一行描述,包含一个地图名称(由字母组成的字符串,不含前导、尾随或嵌入空格)和两对实数坐标 \((x_1,y_1)\)、\((x_2,y_2)\),表示该地图的两个对角点。
  • 单独一行、全部大写的单词 LOCATIONS 引入一个包含一个或多个地点的集合。在该标题之后,每个地点由一行描述,包含一个地点名称(由字母组成的字符串,不含前导、尾随或嵌入空格)和一对实数坐标 \((x,y)\),表示该地点的中心。
  • 单独一行、全部大写的单词 REQUESTS 引入一个包含零个或多个请求的集合。在该标题之后,每个请求由一行描述,包含一个地点名称(由字母组成的字符串,不含前导、尾随或嵌入空格),后跟一个正整数,表示该地点所期望的细节级别。
  • 单独一行、全部大写的单词 END 终止文件。

请求之前的所有地图和地点数据都是有效的。不会出现重复地图。处理一个有效请求的结果,是包含给定地点且在给定细节级别下的地图名称(使用上述并列打破规则)。无效请求可能由以下原因导致:请求未知地点名称、地点不出现在任何地图中,或细节级别超过包含该地点的不同面积地图数量。

输出格式:

每个请求都必须回显到输出中。如果请求有效,则显示满足该请求的地图名称。如果该地点不在任何地图中,则显示相应提示信息。如果该地点在地图上但细节级别过大,则显示具有最小可用面积(即最大可能细节级别)的地图名称。

数据范围:

题目没说数据范围。

输入样例:

MAPS
BayArea -6.0 12.0 -11.0 5.0
SantaClara 4.0 9.0 -3.5 2.5
SanJoseRegion -3.0 10.0 11.0 3.0
CenterCoast -5.0 11.0 1.0 -8.0
SanMateo -5.5 4.0 -12.5 9.0
NCalif -13.0 -7.0 13.0 15.0
LOCATIONS
Monterey -4.0 2.0
SanJose -1.0 7.5
Fresno 7.0 0.1
SanFrancisco -10.0 8.6
SantaCruz -4.0 2.0
SanDiego 13.8 -19.3
REQUESTS
SanJose 3
SanFrancisco 2
Fresno 2
Stockton 1
SanDiego 2
SanJose 4
SantaCruz 3
END

输出样例:

SanJose at detail level 3 using SanJoseRegion
SanFrancisco at detail level 2 using BayArea
Fresno at detail level 2 no map at that detail level; using NCalif
Stockton at detail level 1 unknown location
SanDiego at detail level 2 no map contains that location
SanJose at detail level 4 using SantaClara
SantaCruz at detail level 3 no map at that detail level; using CenterCoast

(这题的输入输出太复杂了,就贴一下输入输出样例)

阅读理解 + 大模拟。

大意就是给你平面直角坐标系上的几个地图(矩形)和地点(点),每次询问地点和缩放等级,

然后给出与查询结果最接近的地图是哪张,查询不存在的地点或者缩放等级都是不合法的,差不多这个意思。

坑点在于

  • 原题面是英语的,加大了阅读理解的难度

  • 它没说右下角是哪里,实际上是矩形离 \((0,0)\) 距离最远的那个顶点。

    也就是说它的坐标系画出来, \(x\) 轴正方向向右侧,\(y\) 轴正方向向下侧。

  • 本题有很多浮点数运算和比较,可以写一个浮点数比较的函数手动控制误差。

  • 它先是说地点和地图都可能存在重名情况,后面又说地图和地点不会重复。

    因此可以理解为:地点数据是唯一的;地图数据可以存在重名地图,但不存在完全一样的地图。

  • 没给数据范围,不知道开多大数组,建议使用 vector 。

有一些优化用的小技巧,比如地图矩形的面积和高宽比是确定的,可以预处理出来。

还有两点距离公式,由于我们只需要比较两个距离的大小,所以不需要开根号(否则降低精度出错概率增加)

分析一下复杂度。记地图有 \(n\) 个,地点有 \(m\) 个,询问有 \(q\) 个,则复杂度为 \(O(n + m + qn\log n)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
typedef pair<double, double> pdd;
const double eps = 1e-18;
int dcmp(double x) { if(abs(x) <= eps) return 0; else return x > eps ? 1 : -1; }
template<typename T> void up(T &x, T y) { x < y ? x = y : 0; }
template<typename T> void down(T &x, T y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)())
#define Fi first
#define Se second

string str;
struct Maps { string name; double x1, y1, x2, y2, r, S; };

vector<Maps> vec;
unordered_map<string, pdd> mp;

double dis(double x1, double y1, double x2, double y2)
{
    const double x = (x1 - x2) * (x1 - x2);
    const double y = (y1 - y2) * (y1 - y2);
    return x + y;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> str;
    while(cin >> str && str != "LOCATIONS") // "MAPS"
    {
        static double x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
        const double _r = abs(y1 - y2) * 1.0 / abs(x1 - x2);
        const double _S = abs(x1 - x2) * abs(y1 - y2);
        vec.push_back((Maps){str, x1, y1, x2, y2, _r, _S});
    }
    while(cin >> str && str != "REQUESTS") // "LOCATIONS"
    {
        static double x, y; cin >> x >> y;
        mp[str] = (pdd){x, y};
    }
    while(cin >> str && str != "END") // "REQUESTS"
    {
        static int idx; cin >> idx;
        cout << str << " at detail level " << idx << ' ';
        if(mp.find(str) == mp.end()) { cout << "unknown location\n"; continue; }
        pdd qwq = mp[str];
        static double x0, y0; x0 = qwq.Fi, y0 = qwq.Se;
        static vector<Maps> tmp; tmp.clear();
        static vector<double> S; S.clear();
        for(auto i : vec)
        {
            const bool _1 = min(i.x1, i.x2) <= x0 && x0 <= max(i.x1, i.x2);
            const bool _2 = min(i.y1, i.y2) <= y0 && y0 <= max(i.y1, i.y2);
            if(_1 && _2) { tmp.push_back(i); S.push_back(i.S); }
        }
        if(tmp.empty()) { cout << "no map contains that location\n"; continue; }
        sort(S.begin(), S.end(), greater<double>());
        int tot = unique(S.begin(), S.end()) - S.begin();
        if(tot < idx) { cout << "no map at that detail level; "; idx = tot; }
        static vector<Maps> ans; ans.clear();
        for(auto i : tmp) if(dcmp(i.S - S[idx - 1]) == 0) ans.push_back(i);
        static auto f = [](const Maps &a, const Maps &b)
        {
            double c1 = dis((a.x1 + a.x2) / 2.0, (a.y1 + a.y2) / 2.0, x0, y0);
            double c2 = dis((b.x1 + b.x2) / 2.0, (b.y1 + b.y2) / 2.0, x0, y0);
            int t = dcmp(c1 - c2); if(t != 0) return t < 0;
            c1 = abs(a.r - 0.75); c2 = abs(b.r - 0.75);
            t = dcmp(c1 - c2); if(t != 0) return t < 0;
            c1 = dis(max(a.x1, a.x2), max(a.y1, a.y2), x0, y0);
            c2 = dis(max(b.x1, b.x2), max(b.y1, b.y2), x0, y0);
            t = dcmp(c1 - c2); if(t != 0) return t > 0;
            return min(a.x1, a.x2) < min(b.x1, b.x2);
        };
        sort(ans.begin(), ans.end(), f);
        cout << "using " << ans[0].name << '\n';
    }
    return 0;
}