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的更多相关文章

  1. 【算法学习记录-排序题】【PAT A1012】The Best Rank

    To evaluate the performance of our first year CS majored students, we consider their grades of three ...

  2. 【算法学习记录-排序题】【PAT A1025】PAT Ranking

    Programming Ability Test (PAT) is organized by the College of Computer Science and Technology of Zhe ...

  3. 【算法学习记录-排序题】【PAT A1062】Talent and Virtue

    About 900 years ago, a Chinese philosopher Sima Guang wrote a history book in which he talked about ...

  4. 算法学习记录-排序——插入排序(Insertion Sort)

    插入排序: 在<算法导论>中是这样描述的 这是一个对少量元素进行排序的有效算法.插入排序的工作机理与打牌时候,整理手中的牌做法差不多. 在开始摸牌时,我们的左手是空的,牌面朝下放在桌子上. ...

  5. 算法学习记录-排序——冒泡排序(Bubble Sort)

    冒泡排序应该是最常用的排序方法,我接触的第一个排序算法就是冒泡,老师也经常那这个做例子. 冒泡排序是一种交换排序, 基本思想: 通过两两比较相邻的记录,若反序则交换,知道没有反序的记录为止. 例子: ...

  6. 算法学习记录-排序——选择排序(Simple Selection Sort)

    之前在冒泡排序的附录中提到可以在每次循环时候,不用交换操作,而只需要记录最小值下标,每次循环后交换哨兵与最小值下标的书, 这样可以减少交换操作的时间. 这种方法针对冒泡排序中需要频繁交换数组数字而改进 ...

  7. PAT A1016 Phone Bills (25 分)——排序,时序

    A long-distance telephone company charges its customers by the following rules: Making a long-distan ...

  8. 算法学习记录-图——应用之拓扑排序(Topological Sort)

    这一篇写有向无环图及其它的应用: 清楚概念: 有向无环图(DAG):一个无环的有向图.通俗的讲就是从一个点沿着有向边出发,无论怎么遍历都不会回到出发点上. 有向无环图是描述一项工程或者系统的进行过程的 ...

  9. 算法学习 拓扑排序(TopSort)

    拓扑排序 一.基本概念 在一个有向无环图(Directed Acyclic Graph, DAG)中,规定< u,v > 表示一条由u指向v的的有向边.要求对所有的节点排序,使得每一条有向 ...

随机推荐

  1. 使用INF创建CSR文件

    公司要为一个英国的客户提供由HTTP升级到HTTPS的服务,于是接触到了申请SSL证书这方面的内容. 一.总的来说,申请证书需要两步,一是创建CSR文件,二是在证书提供商购买证书并将CSR文件发给证书 ...

  2. Spring学习笔记-面向切面(AOP)-04

    什么是面向切面编程 先大概了解一下部分术语 横切关注点:软件开发中,散布于多出的功能称为横切关注点(cross-cutting concern),简单的可以描述为可以影响应用多处的功能,比如日志.安全 ...

  3. Error: cannot fetch last explain plan from PLAN_TABLE

    最近遇到了错误"Error: cannot fetch last explain plan from PLAN_TABLE",于是稍微研究了一下哪些场景下碰到这种错误,具体参考下面 ...

  4. 剑指offer-面试题26-树的子结构-二叉树

    /* 题目: 输入两棵二叉树A和B,判断B是不是A的子树. */ /* 思路: 1.注意浮点数大小的判断. 2.判断树A的某个节点是否和树B的根节点是否相同, 若相同,则判断以A该节点为根节点是否包含 ...

  5. Linux更改时区

    在下午查看系统时间,发现时间竟然是凌晨2点过: [root@node01 ~]# date Sat Jul 20 02:34:29 EDT 2019 开始以为是时间不是24小时进制的,百度了一下,参考 ...

  6. springboot~工作流activiti的搭建

    概念 工作流产品使用activiti的算是比较多了,自带了一套UI界面,可以直接使用,用来设计流程,下面简单总结一下它的步骤: 1 设计模型 2 发布为流程,一个模型可以发布多个版本的流程 3 建立一 ...

  7. phpstorm安装bootstrap插件

    一个插件可以很好的让我们工作节约时间 Bootstrap,来自 Twitter,是目前最受欢迎的前端框架.Bootstrap 是基于 HTML.CSS.JAVASCRIPT 的,它简洁灵活,使得 We ...

  8. psp 周计划2

    日期\时间 开始时间 结束时间 中断时间 净时间 活动 备注 12/3 9:00 11:30 10:30 120分钟 自习,练习 教室 14:00 16:30 15:30 80分钟 练习 中午休息 1 ...

  9. IP地址分类及其相关计算问题

    IP地址分类及其相关计算问题 公网IP和子网IP 公网IP: • A类:1.0.0.0 到 127.255.255.255 主要分配 给大量主机而局域网网络数量较少的大型网络 • B类:128.0.0 ...

  10. 杭电oj2037——今年暑假不AC(java实现)

    思路:标准贪心 先把所有思路列出来: 1.优先选择开始时间最早的,经分析,不可行 2.优先选择持续时间最短的,经分析,不可行 3.优先选择结束时间最早的,经分析,可行 然后根据第三种思路实现代码就好 ...