## [$AcWing$ $1162$. 公交换乘](https://www.acwing.com/problem/content/description/1164/) ### 一、题目描述 著名旅游城市 $B$ 市为了鼓励大家采用公共交通方式出行,推出了一种地铁换乘公交车的优惠方案: 在搭乘一次地铁后可以获得一张优惠票,有效期为 $45$ 分钟,在有效期内可以消耗这张优惠票,免费搭乘一次票价不超过地铁票价的公交车。在有效期内指开始乘公交车的时间与开始乘地铁的时间之差小于等于 $45$ 分钟,即:$tbus$−$tsubway$≤$45$搭乘地铁获得的优惠票可以累积,即可以连续搭乘若干次地铁后再连续使用优惠票搭乘公交车,但每次搭乘公交车只能使用一张优惠券。 搭乘公交车时,如果可以使用优惠票一定会使用优惠票;如果有多张优惠票满足条件,则优先消耗获得最早的优惠票。 现在你得到了小轩最近的公共交通出行记录,你能帮他算算他的花费吗? **输入格式** 第一行包含一个正整数 $n$,代表乘车记录的数量。 接下来的 $n$ 行,每行包含 $3$ 个整数,相邻两数之间以一个空格分隔。第 $i$ 行的第 $1$ 个整数代表第 $i$ 条记录乘坐的交通工具,$0$ 代表地铁,$1$ 代表公交车;第 $2$ 个整数代表第 $i$ 条记录乘车的票价 $pricei$ ;第三个整数代表第 $i$ 条记录开始乘车的时间 $ti$(距 $0$ 时刻的分钟数)。 我们保证出行记录是按照开始乘车的时间顺序给出的,且 不会有两次乘车记录出现在同一分钟。 **输出格式** 有一行,包含一个正整数,代表小轩出行的总花费。 **数据范围** 对于 $30$% 的数据,$n$≤$1000$,$t_i$≤$10^6$。 另有 $15$% 的数据,$t_i$≤$10^7$,$price_i$ 都相等。 另有 $15$% 的数据,$t_i$≤$10^9$,$price_i$ 都相等。 对于 $100$% 的数据,$n$≤$10^5$,$t_i$≤$10^9$,$1$≤$price_i$≤$1000$。 注意,本题采用官方比赛实际数据,$t_i$ 的真实范围为 $t_i$≤$10^7$,特此声明。 **输入样例**1: ```cpp {.line-numbers} 6 0 10 3 1 5 46 0 12 50 1 3 96 0 5 110 1 6 135 ``` **输出样例**1: ```cpp {.line-numbers} 36 ``` **输入样例**2: ```cpp {.line-numbers} 6 0 5 1 0 20 16 0 7 23 1 18 31 1 4 38 1 7 68 ``` **输出样例**2: ```cpp {.line-numbers} 32 ``` ### 二、题目解析 > 这道题目有个限制很重要: 不会有两次乘车记录出现在同一分钟 这就说明对于每个公交车来说,所能使用的优惠券不会超过$45$张。即我们可以完美避开各种复杂的数据结构优化了。 因此我们可以从前往后扫描每条记录,同时用一个队列维护当前车次可以使用的优惠券区间: 如果当前记录是火车,则加入维护的优惠券区间; 如果当前记录是公交车,则线性扫描一遍队列中所有优惠券,找到第一个未被使用过的且大于等于当前价格的优惠券即可; 可以用一个$bool$数组对优惠券判重,以保证每张优惠券最多只被用一次。 #### 时间复杂度 最坏情况下,对于每条公交车记录都需要遍历$45$张优惠券,因此时间复杂度是 $O(45n)$。 ### 三、实现代码 ```cpp {.line-numbers} #include using namespace std; const int N = 1e5 + 10; int n; struct Ticket { int f; // 0 代表地铁,1 代表公交车 int p; // 第 2 个整数代表第 i 条记录乘车的票价 price[i] int t; // 第三个整数代表第 i 条记录开始乘车的时间 ti(距 0 时刻的分钟数) } a[N]; int ans; int main() { cin >> n; for (int i = 1; i <= n; i++) cin >> a[i].f >> a[i].p >> a[i].t; for (int i = 1; i <= n; i++) { if (a[i].f == 0) ans += a[i].p; else { bool flag = false; // 加入了优化点,因为一个站点最少1分钟,所以如果在45分钟以内有效的话,则最多是向前45个站点提供的优惠卷 // 如果,成功将O(N^2)的时间复杂度,优化为O(N*45)的时间复杂度,因为N高达1e5,所以可以大大降低运行时长 for (int j = max(i - 45, 1); j < i; j++) { if (a[j].f == 0 && a[i].t - a[j].t <= 45 && a[j].p >= a[i].p) { flag = true; a[j].f = -1; // 优惠票已使用 break; } } if (!flag) ans += a[i].p; } } printf("%d\n", ans); return 0; } ```