【算法学习记录-排序题】【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的的有向边.要求对所有的节点排序,使得每一条有向 ...
随机推荐
- FirstJavaWeb
(未完成)出错有点多,Tomcat的配置也出问题了,Tomcat突然找不到jdk了,好像是我之前下载过一回然后没卸干净有残留文件,还有连接数据库的代码也有问题. <%@page im ...
- Unsupervise-learning-notes
K-means 数据是没有label的,按照数据之间的相似性进行分类 原理and步骤 是随机选取K个对象作为初始的聚类中心, 计算每个对象与各个种子聚类中心之间的距离,把每个对象分配给距离它最近的聚类 ...
- PAT (Advanced Level) Practice 1152 Google Recruitment (20 分)
In July 2004, Google posted on a giant billboard along Highway 101 in Silicon Valley (shown in the p ...
- 选课系统项目_python
一.功能简要 基本实现以下功能,但是有部分地方由于时间关系并未写,而且并未做细微的完善,大致功能完成.角色:学校.学员.课程.讲师要求:1. 创建北京.上海 2 所学校2. 创建linux , pyt ...
- C++中c_str()的用法
这个函数经常用到,总是记不住,记下来,方便翻阅 c_str()函数返回一个指向正规C字符串的指针,内容与string串相同. C++中c_str()主要用法就是为了与C语言兼容,在C语言中没有stri ...
- Win10下安装tensorflow详细过程
首先声明几点: 安装tensorflow是基于Python的,并且需要从Anaconda仓库中下载. 所以我们的步骤是:先下载Anaconda,再在Anaconda中安装一个Python,(你的电脑里 ...
- Docker最全教程——从理论到实战(二十三)
如何节约云端成本? 上云在大部分情况下就是为了降低成本,在这方面,主流的容器服务基本上都能够有效地降低成本——不仅能够高效自动化的管理和控制容器,而且不需支付Kubernetes 主节点的费用.不过, ...
- 使用U盘装Windows10系统
一.装备工作 使用U盘装系统需要准备以下工具: 8G左右的U盘一个.由于制作启动盘会删除U盘的所有数据,所以重要资料请提前备份. 系统的镜像文件.这里我推荐MSDN, 我告诉你.这里下载的镜像和官方的 ...
- easyui-numberbox后台获取数据后,鼠标一点击就自动清空了
<input type="text" name="txtMeterInitData" id="txtMeterInitData" cl ...
- IOU 选框和真实框重叠部分占两个总框并集的比例
IOU 选框和真实框重叠部分占两个总框并集的比例 IOU 召回率:表示在预测为的正类中,有多少正类被预测为正类 https://blog.csdn.net/qq_36653505/article/de ...