码迷,mamicode.com
首页 > 编程语言 > 详细

【算法学习记录-排序题】【PAT A1016】Phone Bills

时间:2020-01-31 17:22:48      阅读:139      评论:0      收藏:0      [点我收藏+]

标签:from   string   范围   下标   off   records   else   enter   with   

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、结构体

1 struct Record {
2     char name[25];          //用户名
3     int month, dd, hh, mm;  //月,日,时,分
4     bool status;            //是否有有效通话记录
5 } rec[1010], temp;

 

2、排序函数cmp

 1 bool cmp(Record a, Record b) {
 2     int s = strcmp(a.name, b.name);
 3     if (s != 0) {
 4         return s < 0;                        //先按姓名字典序
 5     } else if (a.month != b.month) {
 6         return a.month < b.month;            //同一姓名按月
 7     } else if (a.dd != b.dd) {               
 8         return a.dd < b.dd;                  //同一月按日
 9     } else if (a.hh != b.hh) {
10         return a.hh < b.hh;                 //同一日按时
11     } else {
12         return a.mm < b.mm;                 //同一时按分
13     }
14 }

3、计算资费函数get_ans

 1 void get_ans(int on, int off, int& time, int& money) {
 2     temp = rec[on];                                          //用temp代表on-line的时刻
 3     while (temp.dd < rec[off].dd || temp.hh < rec[off].hh || temp.mm < rec[off].mm) {  //当temp还未到达off-line时刻时
 4         time++;                                            //计费时间自增(单位:分钟)
 5         money += toll[temp.hh];                                   //资费自增
                                                       //toll[24]:存储不同时间段的单位资费
6 temp.mm++;                                           //temp自增,向off-line时刻逼近 7 if (temp.mm >= 60) {                                     //60分钟则进位到1小时 8 temp.mm = 0; 9 temp.hh++; 10 } 11 if (temp.hh >= 24) {                                     //24小时则进位到1天 12 temp.hh = 0; 13 temp.dd++; 14 } 15 } 16 }

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() 具体代码

①读入数据并排序

 1 for (int i = 0; i < 24; i++) {
 2     scanf("%d", &toll[i]);         //读入每小时的资费
 3 }
 4 int n ;
 5 scanf("%d", &n);
 6 char line[10];
 7 for (int i = 0; i < n; i++) {
 8     scanf("%s", rec[i].name);
 9     scanf("%d:%d:%d:%d", &rec[i].month, &rec[i].dd, &rec[i].hh, &rec[i].mm);
10     scanf("%s", line);
11     if (strcmp(line, "on-line") == 0) {  //若为on-line则状态为true
12         rec[i].status = true;
13     } else {                  //若为off-line则状态为false
14         rec[i].status = false;
15     }
16 }
17 sort(rec, rec + n, cmp);

②两层while循环

 1 int on = 0, off, next;
 2 while (on < n) {                                //外层while:遍历所有用户的全部通话记录
 3     int needPrint = 0;
 4     next = on;
 5     while (next < n && strcmp(rec[on].name, rec[next].name) == 0) {  //当前用户是否存在有效通话记录
 6         if (needPrint == 0 && rec[next].status == true) {
 7             needPrint = 1;
 8         } else if (needPrint == 1 && rec[next].status == false) {
 9             needPrint = 2;
10         }
11         next++;
12     }
13     if (needPrint < 2) {                            //当前用户不存在有效通话记录,continue到下一个用户
14         on = next;
15         continue;
16     }
17     int allMoney = 0;                              //当前用户存在有效通话记录
18     while (on < next) {                              //内层while:遍历单个用户的全部通话记录
19         while (on < next - 1 && !(rec[on].status == true && rec[on + 1].status == false)) {
20             on++;
21         }    
22         off = on + 1;
23         if (off == next) {                           //如果已遍历完此用户全部通话记录,则break内层while
24             on = next;
25             break;
26         }
27         int time = 0, money = 0;
28         get_ans(on, off, time, money);
29         allMoney += money;
30         on = off + 1;
31     }
32 }

 

6、题解

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 int toll[24];
 6 struct Record {
 7     char name[25];
 8     int month, dd, hh, mm;
 9     bool status;
10 } rec[1010], temp;
11 bool cmp(Record a, Record b) {
12     int s = strcmp(a.name, b.name);
13     if (s != 0) {
14         return s < 0;
15     } else if (a.month != b.month) {
16         return a.month < b.month;
17     } else if (a.dd != b.dd) {
18         return a.dd < b.dd;
19     } else if (a.hh != b.hh) {
20         return a.hh < b.hh;
21     } else {
22         return a.mm < b.mm;
23     }
24 }
25 void get_ans(int on, int off, int& time, int& money) {
26     temp = rec[on];
27     while (temp.dd < rec[off].dd || temp.hh < rec[off].hh || temp.mm < rec[off].mm) {
28         time++;
29         money += toll[temp.hh];
30         temp.mm++;
31         if (temp.mm >= 60) {
32             temp.mm = 0;
33             temp.hh++;
34         }
35         if (temp.hh >= 24) {
36             temp.hh = 0;
37             temp.dd++;
38         }
39     }
40 }
41 int main() {
42     for (int i = 0; i < 24; i++) {
43         scanf("%d", &toll[i]);
44     }
45     int n ;
46     scanf("%d", &n);
47     char line[10];
48     for (int i = 0; i < n; i++) {
49         scanf("%s", rec[i].name);
50         scanf("%d:%d:%d:%d", &rec[i].month, &rec[i].dd, &rec[i].hh, &rec[i].mm);
51         scanf("%s", line);
52         if (strcmp(line, "on-line") == 0) {
53             rec[i].status = true;
54         } else {
55             rec[i].status = false;
56         }
57     }
58     sort(rec, rec + n, cmp);
59     int on = 0, off, next;
60     while (on < n) {
61         int needPrint = 0;
62         next = on;
63         while (next < n && strcmp(rec[on].name, rec[next].name) == 0) {
64             if (needPrint == 0 && rec[next].status == true) {
65                 needPrint = 1;
66             } else if (needPrint == 1 && rec[next].status == false) {
67                 needPrint = 2;
68             }
69             next++;
70         }
71         if (needPrint < 2) {
72             on = next;
73             continue;
74         }
75         int allMoney = 0;
76         printf("%s %02d\n", rec[on].name, rec[on].month);
77         while (on < next) {
78             while (on < next - 1 && !(rec[on].status == true && rec[on + 1].status == false)) {
79                 on++;
80             }
81             off = on + 1;
82             if (off == next) {
83                 on = next;
84                 break;
85             }
86             printf("%02d:%02d:%02d ", rec[on].dd, rec[on].hh, rec[on].mm);
87             printf("%02d:%02d:%02d ", rec[off].dd, rec[off].hh, rec[off].mm);
88             int time = 0, money = 0;
89             get_ans(on, off, time, money);
90             allMoney += money;
91             printf("%d $%.2f\n", time, money / 100.0);
92             on = off + 1;
93         }
94         printf("Total amount: $%.2f\n", allMoney / 100.0);
95     }
96     return 0;
97 }

 

【算法学习记录-排序题】【PAT A1016】Phone Bills

标签:from   string   范围   下标   off   records   else   enter   with   

原文地址:https://www.cnblogs.com/Bananice/p/12241798.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!