信息学奥赛一本通 1983:【19CSPJ普及组】公交换乘 | 洛谷 P5661 [CSP-J2019] 公交换乘

【题目链接】

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;
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值