【算法学习记录-排序题】【PAT A1016】Phone Bills
A long-distance telephone company charges its customers by the following rules:
Making a long-distance call costs a certain amount per minute, depending on the time of day when the call is made. When a customer starts connecting a long-distance call, the time will be recorded, and so will be the time when the customer hangs up the phone. Every calendar month, a bill is sent to the customer for each minute called (at a rate determined by the time of day). Your job is to prepare the bills for each month, given a set of phone call records.
Input Specification:
Each input file contains one test case. Each case has two parts: the rate structure, and the phone call records.
The rate structure consists of a line with 24 non-negative integers denoting the toll (cents/minute) from 00:00 - 01:00, the toll from 01:00 - 02:00, and so on for each hour in the day.
The next line contains a positive number N (≤1000), followed by N lines of records. Each phone call record consists of the name of the customer (string of up to 20 characters without space), the time and date (mm:dd:hh:mm
), and the word on-line
or off-line
.
For each test case, all dates will be within a single month. Each on-line
record is paired with the chronologically next record for the same customer provided it is an off-line
record. Any on-line
records that are not paired with an off-line
record are ignored, as are off-line
records not paired with an on-line
record. It is guaranteed that at least one call is well paired in the input. You may assume that no two records for the same customer have the same time. Times are recorded using a 24-hour clock.
Output Specification:
For each test case, you must print a phone bill for each customer.
Bills must be printed in alphabetical order of customers' names. For each customer, first print in a line the name of the customer and the month of the bill in the format shown by the sample. Then for each time period of a call, print in one line the beginning and ending time and date (dd:hh:mm
), the lasting time (in minute) and the charge of the call. The calls must be listed in chronological order. Finally, print the total charge for the month in the format shown by the sample.
Sample Input:
10 10 10 10 10 10 20 20 20 15 15 15 15 15 15 15 20 30 20 15 15 10 10 10
10
CYLL 01:01:06:01 on-line
CYLL 01:28:16:05 off-line
CYJJ 01:01:07:00 off-line
CYLL 01:01:08:03 off-line
CYJJ 01:01:05:59 on-line
aaa 01:01:01:03 on-line
aaa 01:02:00:01 on-line
CYLL 01:28:15:41 on-line
aaa 01:05:02:24 on-line
aaa 01:04:23:59 off-line
Sample Output:
CYJJ 01
01:05:59 01:07:00 61 $12.10
Total amount: $12.10
CYLL 01
01:06:01 01:08:03 122 $24.40
28:15:41 28:16:05 24 $3.85
Total amount: $28.25
aaa 01
02:00:01 04:23:59 4318 $638.80
Total amount: $638.80
题意:
第一行给出一天中每个小时的资讯费(cents/minute)
第二行给出通话记录的条数N
接下来N行,每行均记录用户名、当前时刻(月:日:时:分)及此时处于通话开始(on-line)或通话结束(off-line)(注:题目保证单个case中一定都在一个月内)
现需要对每个人的有效通话记录进行计费
有效通话记录:在按时间排序所有记录后,对一个用户,若有相邻两条记录,前者为on-line且后者为off-line,则认为此为一条有效通话记录
输出要求:①字典序输出姓名②输出对应的账单月份③输出有效通话记录的起止和时长以及话费④输出总资费Total amount
思路:
0、思考过程
这一题在书中是紧接在【A1012】之后,所以在第一次看到这题时,我立刻想到了和【A1012】样例类似的结构(二维数组)
即纵向按照字典序排,横向按照时间排
姓名 | 通话记录 |
Bob | 01:02:03:04 on-line, 01:03:04:05 off-line, 01:04:05:06 off-line ...... |
Peter | ...... |
··· |
但是再次读题后意识到此题和【A1012】存在一个细微的区别:
【A1012】是写明了(纵向)学生人数<=2000,且(横向)科目数=4
而这题只给出(横向)通话记录<=1000,但是并未给出人数的范围,纵向该开多大无法得知
所以这样的结构设计并不合理
故采用一维数组
1、结构体
struct Record {
char name[]; //用户名
int month, dd, hh, mm; //月,日,时,分
bool status; //是否有有效通话记录
} rec[], temp;
2、排序函数cmp
bool cmp(Record a, Record b) {
int s = strcmp(a.name, b.name);
if (s != ) {
return s < ; //先按姓名字典序
} else if (a.month != b.month) {
return a.month < b.month; //同一姓名按月
} else if (a.dd != b.dd) {
return a.dd < b.dd; //同一月按日
} else if (a.hh != b.hh) {
return a.hh < b.hh; //同一日按时
} else {
return a.mm < b.mm; //同一时按分
}
}
3、计算资费函数get_ans
void get_ans(int on, int off, int& time, int& money) {
temp = rec[on]; //用temp代表on-line的时刻
while (temp.dd < rec[off].dd || temp.hh < rec[off].hh || temp.mm < rec[off].mm) { //当temp还未到达off-line时刻时
time++; //计费时间自增(单位:分钟)
money += toll[temp.hh]; //资费自增
//toll[24]:存储不同时间段的单位资费
temp.mm++; //temp自增,向off-line时刻逼近
if (temp.mm >= ) { //60分钟则进位到1小时
temp.mm = ;
temp.hh++;
}
if (temp.hh >= ) { //24小时则进位到1天
temp.hh = ;
temp.dd++;
}
}
}
4、main() 逻辑分析:
①整体的结构应该是这样:
1)读入数据并排序
2)while循环遍历所有记录,对每个人,查询是否存在有效通话记录
(1)若无,则continue,继续查下一个人
(2)若有,则while循环遍历此人的所有记录,查询所有有效通话记录并计费,查完后break内层的while,继续外层的while循环查下一个人
②如何确认是否存在有效通话记录?
确认的过程是这样的:
无on无off→有on无off→有on有off
*踩到的坑:
先后顺序 | on-line | off-line |
on-line | 1、全是on-line | 2、先off-line后on-line |
off-line | 3、先on-line后off-line | 4、全是off-line |
如果只是标识 有on 和 有off,则可能出现 前部分全都是off-line & 后部分全都是on-line 的边界情况
此时不存在任何有效通话记录
所以要设一个flag:
flag = 0:无on无off
flag = 1:有on无off
flag = 2:有on有off
且flag改变的条件为:
if (flag == 0 && rec[index].status == true)
{ flag = 1; }
if (flag == 1 && rec[index].status == false)
{ flag = 2; }
③使用两个下标on和next
我在最初时只想到使用一个下标index,
即
设下标index,index不断自增
在 是同一个人 即 strcmp(rec[index].name, rec[index + 1].name) == 0 的前提下
当出现 rec[index].status == true && rec[index + 1].status == false 时,为存在有效通话记录
之后应开始遍历此人的所有记录,查询他所有有效通话记录并计费
这看上去是可行的,但我并没有实践
而是参考了书中的设计——使用两个下标on和next
1)在确认某人是否存在有效通话记录的while过程中
on不移动,始终标识某用户的第一条通话记录
其用于确定当前的是同一个人,即 strcmp(rec[on].name, rec[next].name) == 0
在这个前提下确认是否存在有效通话记录
而next则负责不断自增,以检查下一条通话记录
2)在查询此人的所有有效通话记录的while过程中
on负责不断自增,以检查出具体的每条有效通话记录,即 rec[on].status == true && rec[on + 1].status == false
而next不移动,始终标识此用户的最后一条通话记录
其用于确定 off = on + 1 是否已到达此人通话记录的结尾
即 if (off == next) { break; }
5、main() 具体代码
①读入数据并排序
for (int i = ; i < ; i++) {
scanf("%d", &toll[i]); //读入每小时的资费
}
int n ;
scanf("%d", &n);
char line[];
for (int i = ; i < n; i++) {
scanf("%s", rec[i].name);
scanf("%d:%d:%d:%d", &rec[i].month, &rec[i].dd, &rec[i].hh, &rec[i].mm);
scanf("%s", line);
if (strcmp(line, "on-line") == ) { //若为on-line则状态为true
rec[i].status = true;
} else { //若为off-line则状态为false
rec[i].status = false;
}
}
sort(rec, rec + n, cmp);
②两层while循环
int on = , off, next;
while (on < n) { //外层while:遍历所有用户的全部通话记录
int needPrint = ;
next = on;
while (next < n && strcmp(rec[on].name, rec[next].name) == ) { //当前用户是否存在有效通话记录
if (needPrint == && rec[next].status == true) {
needPrint = ;
} else if (needPrint == && rec[next].status == false) {
needPrint = ;
}
next++;
}
if (needPrint < ) { //当前用户不存在有效通话记录,continue到下一个用户
on = next;
continue;
}
int allMoney = ; //当前用户存在有效通话记录
while (on < next) { //内层while:遍历单个用户的全部通话记录
while (on < next - && !(rec[on].status == true && rec[on + ].status == false)) {
on++;
}
off = on + ;
if (off == next) { //如果已遍历完此用户全部通话记录,则break内层while
on = next;
break;
}
int time = , money = ;
get_ans(on, off, time, money);
allMoney += money;
on = off + ;
}
}
6、题解
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int toll[];
struct Record {
char name[];
int month, dd, hh, mm;
bool status;
} rec[], temp;
bool cmp(Record a, Record b) {
int s = strcmp(a.name, b.name);
if (s != ) {
return s < ;
} else if (a.month != b.month) {
return a.month < b.month;
} else if (a.dd != b.dd) {
return a.dd < b.dd;
} else if (a.hh != b.hh) {
return a.hh < b.hh;
} else {
return a.mm < b.mm;
}
}
void get_ans(int on, int off, int& time, int& money) {
temp = rec[on];
while (temp.dd < rec[off].dd || temp.hh < rec[off].hh || temp.mm < rec[off].mm) {
time++;
money += toll[temp.hh];
temp.mm++;
if (temp.mm >= ) {
temp.mm = ;
temp.hh++;
}
if (temp.hh >= ) {
temp.hh = ;
temp.dd++;
}
}
}
int main() {
for (int i = ; i < ; i++) {
scanf("%d", &toll[i]);
}
int n ;
scanf("%d", &n);
char line[];
for (int i = ; i < n; i++) {
scanf("%s", rec[i].name);
scanf("%d:%d:%d:%d", &rec[i].month, &rec[i].dd, &rec[i].hh, &rec[i].mm);
scanf("%s", line);
if (strcmp(line, "on-line") == ) {
rec[i].status = true;
} else {
rec[i].status = false;
}
}
sort(rec, rec + n, cmp);
int on = , off, next;
while (on < n) {
int needPrint = ;
next = on;
while (next < n && strcmp(rec[on].name, rec[next].name) == ) {
if (needPrint == && rec[next].status == true) {
needPrint = ;
} else if (needPrint == && rec[next].status == false) {
needPrint = ;
}
next++;
}
if (needPrint < ) {
on = next;
continue;
}
int allMoney = ;
printf("%s %02d\n", rec[on].name, rec[on].month);
while (on < next) {
while (on < next - && !(rec[on].status == true && rec[on + ].status == false)) {
on++;
}
off = on + ;
if (off == next) {
on = next;
break;
}
printf("%02d:%02d:%02d ", rec[on].dd, rec[on].hh, rec[on].mm);
printf("%02d:%02d:%02d ", rec[off].dd, rec[off].hh, rec[off].mm);
int time = , money = ;
get_ans(on, off, time, money);
allMoney += money;
printf("%d $%.2f\n", time, money / 100.0);
on = off + ;
}
printf("Total amount: $%.2f\n", allMoney / 100.0);
}
return ;
}
【算法学习记录-排序题】【PAT A1016】Phone Bills的更多相关文章
- 【算法学习记录-排序题】【PAT A1012】The Best Rank
To evaluate the performance of our first year CS majored students, we consider their grades of three ...
- 【算法学习记录-排序题】【PAT A1025】PAT Ranking
Programming Ability Test (PAT) is organized by the College of Computer Science and Technology of Zhe ...
- 【算法学习记录-排序题】【PAT A1062】Talent and Virtue
About 900 years ago, a Chinese philosopher Sima Guang wrote a history book in which he talked about ...
- 算法学习记录-排序——插入排序(Insertion Sort)
插入排序: 在<算法导论>中是这样描述的 这是一个对少量元素进行排序的有效算法.插入排序的工作机理与打牌时候,整理手中的牌做法差不多. 在开始摸牌时,我们的左手是空的,牌面朝下放在桌子上. ...
- 算法学习记录-排序——冒泡排序(Bubble Sort)
冒泡排序应该是最常用的排序方法,我接触的第一个排序算法就是冒泡,老师也经常那这个做例子. 冒泡排序是一种交换排序, 基本思想: 通过两两比较相邻的记录,若反序则交换,知道没有反序的记录为止. 例子: ...
- 算法学习记录-排序——选择排序(Simple Selection Sort)
之前在冒泡排序的附录中提到可以在每次循环时候,不用交换操作,而只需要记录最小值下标,每次循环后交换哨兵与最小值下标的书, 这样可以减少交换操作的时间. 这种方法针对冒泡排序中需要频繁交换数组数字而改进 ...
- PAT A1016 Phone Bills (25 分)——排序,时序
A long-distance telephone company charges its customers by the following rules: Making a long-distan ...
- 算法学习记录-图——应用之拓扑排序(Topological Sort)
这一篇写有向无环图及其它的应用: 清楚概念: 有向无环图(DAG):一个无环的有向图.通俗的讲就是从一个点沿着有向边出发,无论怎么遍历都不会回到出发点上. 有向无环图是描述一项工程或者系统的进行过程的 ...
- 算法学习 拓扑排序(TopSort)
拓扑排序 一.基本概念 在一个有向无环图(Directed Acyclic Graph, DAG)中,规定< u,v > 表示一条由u指向v的的有向边.要求对所有的节点排序,使得每一条有向 ...
随机推荐
- Swagger2 @ApiIgnore注解忽略接口在swagger-ui.html中显示
果项目中定义了一个controller,但是我们又不想把这个接口在swagger-ui.html中体现出来怎么办?不要着急,Swagger2已经替我们想到了这个问题,只要把@ApiIgnore放到你想 ...
- Git无法提交branch is currently checked out
报错 git无法提交,提示 ! [remote rejected] master -> master (branch is currently checked out) 原因 初始化没有用git ...
- java9小工具jshell
1.jshell是jdk9引入的小工具 2.启动jshell 在命令行输入jshell 3.使用jshell 比如定义a=10;b=20;输出a+b的结果,有如下两种方法 方法1:代码写在一行,回车直 ...
- jsp环境搭建
jsp开发环境需要JDK与Tomcat,需先下载JDK组件与tomcat组件 JDK地址:https://www.oracle.com/technetwork/java/javase/download ...
- linux100讲——71 if-else判断的使用
1.if-then-else语句: 语法: if [测试条件成立] then 执行相应的命令 else 测试条件不成立,执行相应的命令 fi 结束 示例:vim 9.sh #!/bin/bash #i ...
- linux C++ 读取mysql结果保存
c++读取mysql数据库结果保存 #include <fstream> #include <iomanip> #include <iostream> #inclu ...
- html css二级导航栏
二级导航栏制作: 1.将一级导航栏去除列表样式(list-style:none),并给予浮动,使其横向排列(float:left) 2.给每个li中添加一个<a></a>标签, ...
- 使用ADO.NET 查询和操作数据
一.使用StringBuilder类追加和删除字符串 1.创建StringBuilder类的对象 StringBuilder sb=new StringBuilder("初始字符串值&quo ...
- ms17-010 利用msf的exp和一个扫描工具的复现
0x01简介 永恒之蓝漏洞是方程式组织在其漏洞利用框架中一个针对SMB服务进行攻击的漏洞,该漏洞导致攻击者在目标系统上可以执行任意代码. 攻击对象:win7及win7以下的操作系统且开启了445端口s ...
- [Python]python去除两个txt文件的重复词汇 python 2020.2.10
两个txt文件词汇,用换行符分隔.可以用代码将要处理的文件去掉另一个文件所包含的重复内容. 如: a.txt内容为: 衡山 泰山 西湖 紫禁城 b.txt内容为: 泰山 衡山 长白山 张三丰 将a.t ...