【题目链接】
ybt 1983:【19CSPJ普及组】公交换乘
洛谷 P5661 [CSP-J2019] 公交换乘
【题目考点】
1. 模拟
【解题思路】
-
设置数组tk保存优惠票,优惠票的属性有:获得时间,价格。
设下标st,tk[st]是当前时间下,时间最早的有效的票。当i<st时tk[i]是过期的票。当i>=st时,tk[i]是有效的票。 -
循环输入数据
- 如果是乘坐地铁,那么添加一张优惠票
- 如果是乘坐公交
- 先更新st的值
- 从st位置开始遍历数组tk,寻找第一个价格高于当前公交票价的优惠票,使用该优惠票。
-
算法复杂度分析
因为每次获得优惠票的时间是有序的,因而设置变量st,每次从st开始遍历。相比于每次从头遍历,这样做可以减少对无效票的遍历,降低时间复杂度。
tk[st]的获得时间到当前时间最多45分钟,题中指出“不会有两次乘车记录出现在同一分钟”,因而有效的优惠票不会超过45个,每次遍历有效优惠票的循环次数不会超过45次。
整个程序的复杂度为O(45*n) = O(n)
【题解代码】
解法1:使用数组
#include <bits/stdc++.h>
using namespace std;
#define N 100005
struct Ticket
{
int time;//time:获得票的时间
int price;//price:买地铁票时的价格,即价格小于等于price的公交车可以用这张票
bool isUsed;//标记这张票是否使用过
Ticket(){}
Ticket(int a, int b):time(a),price(b),isUsed(false){}
};
Ticket tk[N];//保存生成的票
int tk_i, st;//tk_i:记录tk数组中待存储的位置 st: i >= st时,tk[i]是有效的票,i < st时,tk[i]是过期的票
int main()
{
int n, cost = 0, type, price, time;// cost:总花费
cin>>n;
for(int i = 0; i < n; ++i)
{
cin>>type>>price>>time;
if(type == 0)//地铁
{
tk[tk_i++] = Ticket(time, price);//添加优惠票
cost += price;
}
else//公交
{
while(st < tk_i && time - tk[st].time > 45)//更新过期票和有效票的分界点,使tk[st]是最早的有效的票
st++;
bool usedTk = false;//是否使用了优惠票
for(int j = st; j < tk_i; ++j)//搜索优惠票,看能用哪张。循环次数不会超过45次。
{
if(tk[j].price >= price && tk[j].isUsed == false)//如果这张票标价比公交票价高,且没用过,那么可以用。
{
usedTk = true;
tk[j].isUsed = true;
break;
}
}
if(usedTk == false)
cost += price;
}
}
cout<<cost;
return 0;
}
解法2:使用链表
- 写法1:手写链表
#include <bits/stdc++.h>
using namespace std;
#define N 100005
struct Ticket
{
int time, price;
Ticket(){}
Ticket(int a, int b):time(a), price(b){}
};
struct Node
{
Ticket val;
int next, pre;
};
Node node[N];
int p, head, tail;
void push_back(Ticket d)
{
int np = ++p;
node[np].val = d;
node[tail].next = np;
node[np].pre = tail;
tail = np;
}
void pop_front()
{
head = node[head].next;
}
int erase(int t)//t:被删除结点地址
{
node[node[t].pre].next = node[t].next;
node[node[t].next].pre = node[t].pre;
if(t == tail)
tail = node[t].pre;
return node[t].next;
}
bool empty()
{
return head == tail;
}
int main()
{
head = ++p;
tail = head;
int n, sum = 0, type, price, time;
cin >> n;
for(int i = 1; i <= n; ++i)
{
cin >> type >> price >> time;
while(empty() == false && time-node[node[head].next].val.time > 45)
pop_front();
if(type == 0)//地铁
{
sum += price;
push_back(Ticket(time, price));
}
else//公交
{
bool useTicket = false;
for(int i = node[head].next; i != 0;)
{
if(node[i].val.price >= price)
{
i = erase(i);
useTicket = true;
break;
}
else
i = node[i].next;
}
if(useTicket == false)
sum += price;
}
}
cout << sum;
return 0;
}
- 写法2:使用STL list
#include <bits/stdc++.h>
using namespace std;
struct Ticket
{
int time, price;
Ticket(){}
Ticket(int a, int b):time(a), price(b){}
};
int main()
{
list<Ticket> tList;
int n, sum = 0, type, price, time;
cin >> n;
for(int i = 1; i <= n; ++i)
{
cin >> type >> price >> time;
while(tList.empty() == false && time-tList.front().time > 45)
tList.pop_front();
if(type == 0)//地铁
{
sum += price;
tList.push_back(Ticket(time, price));
}
else//公交
{
bool useTicket = false;
for(list<Ticket>::iterator it = tList.begin(); it != tList.end();)
{
if(it->price >= price)
{
it = tList.erase(it);
useTicket = true;
break;
}
else
it++;
}
if(useTicket == false)
sum += price;
}
}
cout << sum;
return 0;
}