本文已参与「新人创作礼」活动,一起开启掘金创作之路。
AutoX 安途智行专场竞赛 题解
AutoX-1. 网页瀑布流
网页布局中有一种瀑布流布局方式,表现为参差不齐的多栏布局。随着页面滚动条向下,还会不断加载数据块并附加至当前尾部。页面在加载时遵循以下规则:
- 当有数据块需要加载时,优先加载在高度最短的那一列;
- 若存在多个高度相同且最短的情况,则加载在其中最靠左的那一列
已知当前网页共分割为 num 列,该网页有若干数据块可以加载,block[i] 表示第 i 个数据块的高度。当页面按顺序加载完所有的数据块后,请返回高度最大的那一列的高度。
示例 1:
输入:
num = 3, block = [5,9,8,6]输出:
11解释:如下图所示,返回 11
示例 2:
输入:
num = 2, block = [9,1,1,1,1,1]输出:
9
提示:
0 < num <= 1000 < block.length <= 10^40 < 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]解释:如下表所示
示例 2:
输入:
handle = [[3],[1,10],[1,0],[3],[4],[2,10],[3]]输出:
[-1.00000,5.00000,25.00000,0.00000]
提示:
0 <= handle.length <=10^50 <= handle[i][1] <= 100handle[i][0]仅包含1,2,3,4
思路
正常做是要超时的,因为操作修改有十万次。 假设先执行五万个1操作,然后1操作和4操作轮回执行,若是每次都算一遍,这个是要超时的。
如何优化呢? 观察一下 handle的值的范围,最大也就是[0, 100] 所以,每次方差不会超过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^51 <= days[i] < days[i+1] <= 10^91 <= tickets.length <= 201 <= tickets[i][0] <= 10^51 <= tickets[i][1] <= 10^9
思路
这道题直接贪心做,并不是很好做
但是我们可以直接对他进行dp做。
因为最多的天数不过天,虽然天的间隔可能非常大,但是没关系。
我们只需要使用的时间可以找到 之后的这一天就可以了
这一步,我们可以直接使用二分,二分下一个可以更新的位置就可以了
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]解释:如下图所示:
所有的几何对象都是可互通的,所有蚂蚁都可以到达目的地。
示例 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无法到达,
提示:
2 <= geometry.length <= 10000 <= geometry[i][0],geometry[i][1] <= 10^5对于线段,0 <= geometry[i][2],geometry[i][3] <= 10^5对于正圆形,1 <= geometry[i][2] <= 10^51 <= path.length <= 10000 <= path[i][0], path[i][1] < geometry.length
思路
讲道理,这道题,若是抛开计算几何的部分,其实是一个简单的并查集
假设两个图形没有相交代表不在一个集合内,则我们可以把这些图形划分为很多个集合
然后我们就可以直接使用并查集来 O(1) 的查询两个图形之间的关系
建图部分的话,我们对两两之间进行判断是否在一个集合中 时间复杂度是 的 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;
}
};