「CSP-J2019」交通换乘 题解
转自博客。
一道橙题
,但是会T。
题面
[CSP-J2019] 公交换乘
题目描述
著名旅游城市 B 市为了鼓励大家采用公共交通方式出行,推出了一种地铁换乘公交车的优惠方案:
- 在搭乘一次地铁后可以获得一张优惠票,有效期为 45 分钟,在有效期内可以消耗这张优惠票,免费搭乘一次票价不超过地铁票价的公交车。在有效期内指开始乘公交车的时间与开始乘地铁的时间之差小于等于 45 分钟,即:
\(t_{bus} - t_{subway} \leq 45\) - 搭乘地铁获得的优惠票可以累积,即可以连续搭乘若干次地铁后再连续使用优惠票搭乘公交车。
- 搭乘公交车时,如果可以使用优惠票一定会使用优惠票;如果有多张优惠票满足条件,则优先消耗获得最早的优惠票。
现在你得到了小轩最近的公共交通出行记录,你能帮他算算他的花费吗?
输入格式
输入文件的第一行包含一个正整数 \(n\),代表乘车记录的数量。
接下来的 \(n\) 行,每行包含 3 个整数,相邻两数之间以一个空格分隔。第 \(i\) 行的第 1 个整数代表第 \(i\) 条记录乘坐的交通工具,0 代表地铁,1 代表公交车;第 2 个整数代表第 \(i\) 条记录乘车的票价 \(price_i\) ;第三个整数代表第 \(i\) 条记录开始乘车的时间 \(t_i\)(距 0 时刻的分钟数)。
我们保证出行记录是按照开始乘车的时间顺序给出的,且不会有两次乘车记录出现在同一分钟。
输出格式
输出文件有一行,包含一个正整数,代表小轩出行的总花费。
样例 #1
样例输入 #1
6
0 10 3
1 5 46
0 12 50
1 3 96
0 5 110
1 6 135
样例输出 #1
36
样例 #2
样例输入 #2
6
0 5 1
0 20 16
0 7 23
1 18 31
1 4 38
1 7 68
样例输出 #2
32
提示
【输入输出样例 1 说明】
第一条记录,在第 3 分钟花费 10 元乘坐地铁。
第二条记录,在第 46 分钟乘坐公交车,可以使用第一条记录中乘坐地铁获得的优惠票,因此没有花费。
第三条记录,在第 50 分种花费 12 元乘坐地铁。
第四条记录,在第 96 分钟乘坐公交车,由于距离第三条记录中乘坐地铁已超过 45 分钟,所以优惠票已失效,花费 3 元乘坐公交车。
第五条记录,在第 110 分钟花费 5 元乘坐地铁。
第六条记录,在第 135 分钟乘坐公交车,由于此时手中只有第五条记录中乘坐地铁获得的优惠票有效,而本次公交车的票价为 6 元,高于第五条记录中地铁的票价 5 元,所以不能使用优惠票,花费 6 元乘坐公交车。
总共花费 36 元。
【输入输出样例 2 说明】
第一条记录,在第 1 分钟花费 5 元乘坐地铁。
第二条记录,在第 16 分钟花费 20 元乘坐地铁。
第三条记录,在第 23 分钟花费 7 元乘坐地铁。
第四条记录,在第 31 分钟乘坐公交车,此时只有第二条记录中乘坐的地铁票价高于本次公交车票价,所以使用第二条记录中乘坐地铁获得的优惠票。
第五条记录,在第 38 分钟乘坐公交车,此时第一条和第三条记录中乘坐地铁获得的优惠票都可以使用,使用获得最早的优惠票,即第一条记录中乘坐地铁获得的优惠票。
第六条记录,在第 68 分钟乘坐公交车,使用第三条记录中乘坐地铁获得的优惠票。
总共花费 32 元。
【数据规模与约定】
对于 \(30\%\) 的数据,\(n \leq 1000\),\(t_i \leq 10^6\)。
另有 \(15\%\) 的数据,\(t_i \leq 10^7\),\(price_i\) 都相等。
另有 \(15\%\) 的数据,\(t_i \leq 10^9\),\(price_i\) 都相等。
对于 \(100\%\) 的数据,\(n \leq 10^5\),\(t_i \leq 10^9\),\(1 \leq price_i \leq 1000\)。
PS:其实你可以点这个复制而不用慢慢改格式qaq
解析
很明显,这道题只需要使用一个结构体数组来模拟门票,如果时间在\(45\) 分钟内,即\(ticket_{time} \leq time_i\),且 \(ticket_{price} \leq price_i\),且满足 ticket.can
(票可以用)这三个条件即可,因为要求优先消耗获得最早的优惠票,所以从最前面开始查找就行了。
于是,你很轻易地写出了这个代码:
#include <bits/stdc++.h>
using namespace std;
struct ticket{
int time;
int price;
bool can=1;
};
inline void read(int &n){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar(); //PS:不用快读可能炸
}
n=x*f;
}
int main(){
// freopen("P5661_9.in","r",stdin);
// freopen("P5661_9.ans","w",stdout);
int n,s=0,l=1;
ticket t[114514];
read(n);
for(int i=1;i<=n;i++){
int b,tim,p;
read(b);read(p);read(tim);
if(b==0){
t[l].time=tim;
t[l].price=p;
l++;
s+=p;
}
if(b==1){
int flag=1;
for(int j=1;j<l;j++){
if(t[j].can&&t[j].price>=p&&tim-t[j].time<=45){
t[j].can=0;
flag=0;
break;
}
}
if(flag){
s+=p;
}
}
}
cout<<s;
return 0;
}
恭喜你喜提 \(35\) 分。
我们想想,所有的数据,\(n \leq 10^5\),显然每次去搜索一遍表来找门票,时间复杂度必然是\(O(n^2)\)的,计算次数\(n \leq 10^{25}\),绝对会超时。
怎么优化呢?我们想想,是不是每一次坐公交车的时候找票都只找没过期的票,是吧。因为时间是升序排列的,所以我们每一次找票的时候根本不需要在前面的一堆烂票里面浪费自己的美好年华。我们不如用一个变量 \(X\) 来标记目前第一个没超时(注意不是没有用过)的票,每次找的时候从第 \(X\) 张票开始找满足的就行了。
代码
#include <bits/stdc++.h>
using namespace std;
struct ticket{
int time; //时间
int price; //价值
bool can=1; //能不能用
}t[114514]; //结构体数组
inline void read(int &n){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
} //PS:不用快读,甚至cin也能过,但40+ms的读入浪费,显然不太好
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
n=x*f;
}
int main(){
// freopen("P5661_9.in","r",stdin);
// freopen("P5661_9.ans","w",stdout);
int n,s=0,l=1,x=1;
read(n);
for(int i=1;i<=n;i++){
int b,tim,p;
read(b);read(p);read(tim);
if(b==0){ //地铁,并且往数组里面扔一个票
t[l].time=tim;
t[l].price=p;
l++;
s+=p;
}
if(b==1){ //公交
int flag=1; //用票与否
for(int j=x;j<l;j++){
if(t[j].can&&t[j].price>=p&&tim-t[j].time<=45){ //满足三个条件就能用
t[j].can=0;
flag=0; //用票
break; //找到能用的票就退出
}
if(tim-t[j].time>45){
x=j; //超时了前面的就不要了
}
}
if(flag){
s+=p; //不用票
}
}
}
cout<<s;
return 0;
}