公交换乘C++

题目:


 

 

 


样例解释:

样例#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 元。
样例#3:
第一条记录,在第 1 分钟花费 5 元乘坐地铁。
第二条记录,在第 16 分钟花费 6 元乘坐地铁。
第三条记录,在第 23 分钟花费 10 元乘坐地铁。
第四条记录,在第 47 分钟乘坐公交车。此时由于距离第一条记录中乘坐地铁已超过 45 分钟,所以优惠票已失效;第二,三条记录中的优惠票有效,但本次公交票价为 7 元,高于第二条的优惠票,只有第三条记录中的优惠票可以使用,所以使用第三条记录的优惠票。
第五条记录,在第 50 分钟乘坐公交车,此时第二,三条记录的优惠票有效,但第三条记录的优惠票已经使用,第二条记录的优惠票高于本次公交票价,所以使用第二条记录的优惠票。
第六条记录,在第 55 分钟乘坐公交车,第一条记录的优惠票已失效,第二,三条记录的优惠票已使用,花费 10 元乘坐公交车。
总共花费 31 元。


 思路:

用一个数组来装所有的收集到的赠票。每当坐地铁的时候,就直接花钱,然后获得一张赠票,放到数组里面。每当坐公交的时候,看看数组里面有没有时间合适,价格小于现在公交票价的赠票,并且没用过的赠票,直接用时间最早的那一张就可以了。

每张赠票要存三个信息,因此用一个结构体来存。数据不超过105105,因此开一个105105长度的数组。

const int MAXN = 100005;
struct Ticket {
    //赠票的价格,最晚使用时间和是否需用过
    int price, time, used;
} q[MAXN];//赠票盒子

然而,出题人会这么善良么?NO,NO,NO,显然不会。我们想想极限情况什么样子,极限数据105105,假设开始的时候全坐地铁,坐了5∗1045∗104 次以后,我们的盒子里面有好多票啊。后面全坐公交,要坐5∗1045∗104 次,每次都在这个大盒子里面找合适的票,复杂度O(n2)O(n2),总计算量2.5∗1092.5∗109,我们在超时的边缘疯狂试探啊。

怎么优化呢?关键点在于题中说每次坐车开始时间都不重合,而且45分钟票就过期。所以理论上来讲,盒子里最多也就有45张没过期的票。大量的票都是已经过期了的,没必要从头扫一遍数组,在大量过期的票中浪费宝贵的青春。所以我们想到类似手写队列的方法,定义一个head变量和一个tail变量,分别表示目前还没过期的第一张票的位置,和下一张新的赠票在数组里要插入的位置,每次从head到小于tail循环即可。每次最多循环45次,总共105105次循环,肯定不会超时。

 其余细节看代码注释吧。不要复制粘贴哦,理解题目才是王道!!!


 

#include <iostream>
using namespace std;
const int MAXN = 100005;
struct Ticket {
    //赠票的价格,最晚使用时间和是否需用过
    int price, time, used;
} q[MAXN];//赠票盒子
int head, tail, n, cost;

int main() {
    cin >> n;
    for (int i = 0; i < n; ++i) {
        int op, price, time;
        //输入每次坐车的种类,价格和发车时间
        cin >> op >> price >> time;
        if (op == 0) {
            //如果是坐地铁,直接把价格加到cost里面
            cost += price;
            //新一张赠票插入数组末尾,这张票的最晚使用时间是当前时间+45
            q[tail].time = time + 45;
            //赠票面额就是地铁票价
            q[tail++].price = price;
        } else {
            //先用一个循环把过期票扔掉
            while (head < tail && q[head].time < time) {
                head++;
            }
            bool found = false;//表示是否有合适的赠票,先假设没有
            for (int j = head; j < tail; ++j) {
                //循环所有剩余的票,这些一定都没过期,因为题目中时间是按顺序给我们的
                if (q[j].price >= price && q[j].used == 0) {
                    //如果价格合适,并且没用过,标记找到了,这张票标记用过
                    found = true;
                    q[j].used = 1;
                    break;
                }
            }
            //如果没找到合适的赠票,老老实实花钱买吧
            if (!found) cost += price;
        }
    }
    cout << cost << endl;
    return 0;
}

本程序为GIS和路径算法的测试程序,路径仅供参考。 算法描述:根据公交站点构造出虚拟含换乘边在内的公交路网,共有约22万个路段,2.3万个站点参与路径计算。根据不同的公交线路的速度赋不同的权值,对不同的换乘进行处理,求到最优的路径。换乘确定在400米之内+等车时间,求出最佳换乘。 新增功能 公交网路(含地铁城铁)交通最优路径计算。 为了使计算出的路径尽可能合理,程序换乘部分扩展的大量的路段,所以计算过程稍慢。 操作简单,用鼠标分别选中起始点和目的地点,系统自动计算最佳换乘路线。 由于公交数据变化很大,计算出的换乘路径仅供参考。 图层控制,用户可以通过图层选项,根据喜好,定制地图显示内容。 界面下部信息框显示相关公交路线,路径信息,和地址附近的公交站点信息。 区域设施,按住鼠标左键在地图上画方框包含查询区域,显示区域内的各类地址名称。 包含大量的学校、机构、企事业等地址,共有1.7万多个个地址可供查询。支持模糊查询方式,在关键词栏中写入地址关键词,点击查询按钮,会在下面列表框中显示所有与该关键词相近的地址,鼠标双击相应的地址,该地址会在地图上显示出来,并在下面信息框中显示附近的公交车站及公交线路。 程序说明 程序中所涉及的算法及核心技术全部采用北京工业大学通研究中心陈艳艳的算法和思路。 程序采用VC++语言在windows平台从底层开发,没有使用其他商用GIS组件或支持包。程序运行简洁、高效。 现有功能(整个北京地区): 支持多个图层:绿地、河流、道路、行政区、交通区、村乡地址、公交站点等。 地图浏览:鼠标滚轮放大、缩小。按鼠标左键拖动地图移动。 支持地图中交通对象的信息交互查询。 通过输入关键词实现快速地址及公交线路查询,并在图上显示。 选择下拉框,选择公交线路查询 信息查询: 快速地址及公交查询,在信息框输入栏中输入地址关键词,即可查出与该关键词有关的所有地址,鼠标双击列表框中列出的地址,可以动态显示该地址在地图上的位置。关键词如输入“52”可得到所有包含52的公交站点。 路段信息查询、修改,选取对应的菜单项,用鼠标点中某一路段,单击鼠标左键,弹出对话框,显示这个路段的信息包括路段名、长度、速度。 支持GPS定位,默认串口com1,每秒位数(bps):4800. 车辆行驶轨迹在地图上实时显示,轨迹存储、装载。 支持键盘方式:方向键上下左右移动地图,home,end放大缩小地图。 存在问题: 同样的起始点路径可能不同:由于同名的公交车站点可能在相近的不同地方,分别属于不同的公交线路。起始点的选择一般采用搜索到的第一个名称。这就造成同样的起始点而搜索的路径不同。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值