AutoX 安途智行专场竞赛

259 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。
AutoX 安途智行专场竞赛 题解

AutoX-1. 网页瀑布流

网页布局中有一种瀑布流布局方式,表现为参差不齐的多栏布局。随着页面滚动条向下,还会不断加载数据块并附加至当前尾部。页面在加载时遵循以下规则:

  • 当有数据块需要加载时,优先加载在高度最短的那一列;
  • 若存在多个高度相同且最短的情况,则加载在其中最靠左的那一列

已知当前网页共分割为 num 列,该网页有若干数据块可以加载,block[i] 表示第 i 个数据块的高度。当页面按顺序加载完所有的数据块后,请返回高度最大的那一列的高度。

示例 1:

输入:num = 3, block = [5,9,8,6]

输出:11

解释:如下图所示,返回 11
image.png

示例 2:

输入:num = 2, block = [9,1,1,1,1,1]

输出:9

提示:

  • 0 < num <= 100
  • 0 < block.length <= 10^4
  • 0 < block[i] <= 10^3

思路

优先队列,贪心的找出最小的一列,进行放置

代码

class Solution {
public:
    int getLengthOfWaterfallFlow(int num, vector<int>& block) {
        int ans = 0;
        priority_queue<int, vector<int>, greater<int>> que;
        for (int i = 0; i < num; i ++) que.push(0);
        for (int i = 0; i < block.size(); i ++) {
            auto t = que.top(); que.pop();
            que.push(block[i] + t);
        }
        while (que.size()) ans = max(ans, que.top()), que.pop();
        return ans;
    }
};

AutoX-2. 蚂蚁王国的蜂蜜

蚂蚁王国的蜂蜜专家旺财最近在研究蜂蜜的价格,为了估算出真实的蜂蜜价格,旺财以所有用有效报价的平均值作为蜂蜜均价,稳定的报价往往方差也比较小。因为情报具有时效性,所以每隔一段时间,旺财也会删除一些老旧报价。
因为计算平均值和方差对于蚂蚁是一个困难的问题,所以旺财希望你帮他设计一个系统,handle[i] = [type] 或 [type, value] 表示对于旺财的第 i 次的操作有:

  • 若 type 为 1,表示获取了一份价格为 value 的报价
  • 若 type 为 2,表示删除了一个价格为 value 的报价
  • 若 type 为 3,表示计算当前蜂蜜的均价;若当前不存在任何有效报价,返回 -1
  • 若 type 为 4,表示计算当前价格的方差;若当前不存在任何有效报价,返回 -1

请按操作的顺序,依次返回所有计算均价方差的结果。

提示:

  • 用例保证所有删除的报价都是有效的。

示例 1:

输入:handle = [[1,1],[1,2],[1,3],[1,2],[3],[4],[2,1],[2,2],[2,3],[3],[4]]

输出:[2.00000,0.50000,2.00000,0.00000]

解释:如下表所示
image.png

示例 2:

输入:handle = [[3],[1,10],[1,0],[3],[4],[2,10],[3]]

输出:[-1.00000,5.00000,25.00000,0.00000]

提示:

  • 0 <= handle.length <=10^5
  • 0 <= handle[i][1] <= 100
  • handle[i][0] 仅包含 1,2,3,4

思路

正常做是要超时的,因为操作修改有十万次。 假设先执行五万个1操作,然后1操作和4操作轮回执行,若是每次都算一遍,这个是要超时的。
如何优化呢? 观察一下 handle的值的范围,最大也就是[0, 100] 所以,每次方差不会超过100,总体复杂度不会超过 105×10010^5 \times 100
所以可以直接做

代码

class Solution {
public:
    vector<double> honeyQuotes(vector<vector<int>>& handle) {
        vector<double> ans;
        int back[101] = {0};
        int sum = 0, cnt = 0;
        for (auto v : handle) {
            if (v[0] == 1) {
                sum += v[1], cnt ++;
                back[v[1]] ++;
            } else if (v[0] == 2) {
                sum -= v[1], cnt --;
                back[v[1]] --;
            } else if (v[0] == 3) {
                if (cnt) ans.push_back(sum * 1.0 / cnt);
                else ans.push_back(-1);
            } else if (v[0] == 4){
                if (!cnt) {
                    ans.push_back(-1);
                    continue;
                }
                double m = sum * 1.0 / cnt, s = 0;
                for (int i = 0; i <= 100; i ++) {
                    s += back[i] * (m - i) * (m - i);
                }
                s /= cnt;
                ans.push_back(s);
            }
        }
        return ans;
    }
};

AutoX-3. 出行的最少购票费用

航空公司向经常乘坐飞机的乘客们提供了一些商务套票,tickets[i] = [duration_i, price_i],表示第 i 种套票的有效天数价格

例如:乘客购买了有效天数为 n 的套票,则该套票在第 date ~ date+n-1 天期间都可以使用。

现有一名乘客将在未来的几天中出行,days[i] 表示他第 i 次出行的时间,如果他选择购买商务套票,请返回他将花费的最少金额。

注意:

  • 输入不存在多个有效天数相同的套票。

示例 1:

输入:
days = [1,2,3,4]
tickets = [[1,3],[2,5],[3,7]]

输出: 10

解释:可以买一张一天有效期的票和一张三天有效期的票;或买两张两天有效期的票;总票价均为10

示例 2:

输入:
days = [1,4,5]
tickets = [[1,4],[5,6],[2,5]]

输出: 6

解释:买一张 5 天有效期的票;总票价为6

提示:

  • 1 <= days.length <= 10^5
  • 1 <= days[i] < days[i+1] <= 10^9
  • 1 <= tickets.length <= 20
  • 1 <= tickets[i][0] <= 10^5
  • 1 <= tickets[i][1] <= 10^9

思路

这道题直接贪心做,并不是很好做
但是我们可以直接对他进行dp做。
因为最多的天数不过10510^5天,虽然天的间隔可能非常大,但是没关系。
我们只需要使用lognlogn的时间可以找到days[i]+tickets[j][0]days[i] + tickets[j][0] 之后的这一天就可以了
这一步,我们可以直接使用二分,二分下一个可以更新的位置就可以了
dp的状态转移这里要选择当前状态推到下一个状态的方式,因为这样转移比较清楚

代码

class Solution {
public:
    long long minCostToTravelOnDays(vector<int>& days, vector<vector<int>>& tickets) {
        vector<long long> dp(days.size() + 1, 1e18); dp[0] = 0;
        for (int i = 0; i < days.size(); i ++) {
            for (auto v : tickets) {
                int idx = lower_bound(begin(days), end(days), days[i] + v[0]) - begin(days);
                dp[idx] = min(dp[idx], dp[i] + v[1]);
            }
        }
        return dp[days.size()];
    }
};

AutoX-4. 蚂蚁爬行

在一张稿纸上画了若干由线条构成的的线段正圆形geometry[i] 表示对于第 i 个线条有:

  • 若 geometry[i].length 为 4 ,表示为一条线段,[x1, y1, x2, y2] 表示该线段的两个端点坐标分别为 (x1,y1) 和 (x2,y2)
  • 若 geometry[i].length 为 3 ,表示为一个正圆形,[x, y, r] 表示其圆心坐标半径分别为 (x,y) 和 r

现有一群小蚂蚁在这些线条上爬行,path[i] = [start, end] 表示第 i 只蚂蚁从第 start 个线条前往第 end 个线条。在爬行过程中,对于任意两个线条,只要有接触(公共点),小蚂蚁就能从一个爬到另一个。请判断这些小蚂蚁能否到达各自的目的地。
示例 1:

输入:
geometry = [[2,5,7,3],[1,1,4,2],[4,3,2]]
path = [[0,1],[1,2],[0,2]]

输出:[true,true,true]

解释:如下图所示:
所有的几何对象都是可互通的,所有蚂蚁都可以到达目的地。
image.png

示例 2:

输入:
geometry = [[4,1,1],[3,2,1],[1,4,5,4]]
path = [[0,1],[2,0]]

输出:[true,false]

解释:如下图所示:
geometry[0] 和 geometry[1] 相接触,geometry[2] 不与任何几何对象接触,因此蚂蚁 1 无法到达,
image.png

提示:

  • 2 <= geometry.length <= 1000
  • 0 <= geometry[i][0],geometry[i][1] <= 10^5
  • 对于线段,0 <= geometry[i][2],geometry[i][3] <= 10^5
  • 对于正圆形,1 <= geometry[i][2] <= 10^5
  • 1 <= path.length <= 1000
  • 0 <= path[i][0], path[i][1] < geometry.length

思路

讲道理,这道题,若是抛开计算几何的部分,其实是一个简单的并查集
假设两个图形没有相交代表不在一个集合内,则我们可以把这些图形划分为很多个集合
然后我们就可以直接使用并查集来 O(1) 的查询两个图形之间的关系
建图部分的话,我们对两两之间进行判断是否在一个集合中 时间复杂度是 n2n ^ 2的 n是100, 可以接受 然后就是图形的判断了。 这里可以分为3种,直线和直线,直线和圆,圆和圆三种情况 分类讨论进行判断即可

代码

struct Point {
    double x, y;
    Point() {}
    Point(int x, int y) : x(x), y(y) {}
};

struct Circle {
    double r, x, y;
    Circle() {};
    Circle(double r, double x, double y) : r(r), x(x), y(y) {}
};

bool judge(Point p1, Point p2, Circle c) {
    bool flag1 =
        (p1.x - c.x) * (p1.x - c.x) + (p1.y - c.y) * (p1.y - c.y) <= c.r * c.r;
    bool flag2 =
        (p2.x - c.x) * (p2.x - c.x) + (p2.y - c.y) * (p2.y - c.y) <= c.r * c.r;
    if (flag1 && flag2)
        return false;
    else if (flag1 || flag2)
        return true;
    else {
        double A, B, C, dist1, dist2, angle1, angle2;
        A = p1.y - p2.y;
        B = p2.x - p1.x;
        C = p1.x * p2.y - p2.x * p1.y;
        dist1 = A * c.x + B * c.y + C;
        dist1 *= dist1;
        dist2 = (A * A + B * B) * c.r * c.r;
        if (dist1 > dist2)
            return false;
        angle1 = (c.x - p1.x) * (p2.x - p1.x) + (c.y - p1.y) * (p2.y - p1.y);
        angle2 = (c.x - p2.x) * (p1.x - p2.x) + (c.y - p2.y) * (p1.y - p2.y);
        if (angle1 > 0 && angle2 > 0)
            return true;
        else
            return false;
    }
}

struct Line {
    double x1;
    double y1;
    double x2;
    double y2;

    Line(int x1, int y1, int x2, int y2) : x1(x1), y1(y1), x2(x2), y2(y2) {}
};

bool intersection(const Line &l1, const Line &l2) {
    if ((l1.x1 > l1.x2 ? l1.x1 : l1.x2) < (l2.x1 < l2.x2 ? l2.x1 : l2.x2) ||
        (l1.y1 > l1.y2 ? l1.y1 : l1.y2) < (l2.y1 < l2.y2 ? l2.y1 : l2.y2) ||
        (l2.x1 > l2.x2 ? l2.x1 : l2.x2) < (l1.x1 < l1.x2 ? l1.x1 : l1.x2) ||
        (l2.y1 > l2.y2 ? l2.y1 : l2.y2) < (l1.y1 < l1.y2 ? l1.y1 : l1.y2)) {
        return false;
    }
    if ((((l1.x1 - l2.x1) * (l2.y2 - l2.y1) - (l1.y1 - l2.y1) * (l2.x2 - l2.x1)) *
         ((l1.x2 - l2.x1) * (l2.y2 - l2.y1) -
          (l1.y2 - l2.y1) * (l2.x2 - l2.x1))) > 0 ||
        (((l2.x1 - l1.x1) * (l1.y2 - l1.y1) - (l2.y1 - l1.y1) * (l1.x2 - l1.x1)) *
         ((l2.x2 - l1.x1) * (l1.y2 - l1.y1) -
          (l2.y2 - l1.y1) * (l1.x2 - l1.x1))) > 0) {
        return false;
    }
    return true;
}

bool CircleIN(double x1, double y1, double r1, double x2, double y2,
              double r2) {
    double s;
    s = sqrt(double((y2 - y1) * (y2 - y1) + (x2 - x1) * (x2 - x1)));
    if (int(s) < r2 + r1 && int(s) > abs(r2 - r1)) return true;
    else return false;
}

class Solution {
    public:
    vector<bool> antPass(vector<vector<int>> &geometry, vector<vector<int>> &path) {
        vector<int> f(geometry.size());
        for (int i = 0; i < f.size(); i++) f[i] = i;
        auto find = [&](auto &&self, int x) -> int {
            if (f[x] != x) f[x] = self(self, f[x]);
            return f[x];
        };
        for (int i = 0; i < geometry.size(); i++)
        for (int j = i + 1; j < geometry.size(); j++) {
            auto a = geometry[i], b = geometry[j];
            int flag = 0;
            if (a.size() == b.size()) {
                if (a.size() == 4) {
                    Line x = {a[0], a[1], a[2], a[3]}, y = {b[0], b[1], b[2], b[3]};
                    flag = intersection(x, y);
                } else {
                    flag = CircleIN(a[0], a[1], a[2], b[0], b[1], b[2]);
                }
            } else {
                if (a.size() == 3) swap(a, b);
                Point x(a[0], a[1]), y(a[2], a[3]);
                Circle z(b[2], b[0], b[1]);
                flag = judge(x, y, z);
            }
            if (flag) {
                int aa = find(find, i), bb = find(find, j);
                if (aa != bb) f[bb] = aa;
            }
        }
        vector<bool> ans;
        for (auto v: path) {
            int aa = find(find, v[0]), bb = find(find, v[1]);
            ans.push_back(aa == bb);
        }
        return ans;
    }
};