题目如下:
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
这道题要求对一个月内多个人的通话记录进行统计,对每个有通话的日期进行统计计算得到话费,并且按照格式输出。
题目的难度主要表现在存储结构的选定、无用数据的排除、输出顺序三个方面。
我最初想到的办法是采用容器分别存储名字和记录,然后分别对对应的记录处理,名字排序,遇到了许多问题。
后来参考了sunbaigui的解决方法,他将名字和记录一起存储在vector中,然后用sort函数对vector排序,排序规则为优先按照字典顺序排列名字,如果名字相同则按照时间顺序(通过对时、分、秒加和来衡量)排列,这样排列完毕后的记录是按照名称从小到大,时间从前到后的顺序,对于统计是十分方便的。
sunbaigui的博客中提到的排除无用记录的方法非常巧妙,由于排完序的具有时间从前到后的顺序,并且一个人的记录是连续分布的,因此只需要从前到后的先找on,再找off,创建一个新的vector,在没有on的情况下遇到on直接压入,如果在有on的情况下碰到on则先弹出上一条无效的on,再把这个on压入;如果on之后碰到了off,并且名字一致(说明还是这个人的记录),则把off压入。这里有个问题,如果最后一条是on,则这个无效记录是无法被排除的(没有机会遇到下一个on来弹出它),因此应该在前面的基础上判断vector中的最后一个元素是否是on,是on则直接弹出,否则说明是有效记录。
接下来只需要对这个新vector(全部是有效的、有序记录)遍历输出即可,从第一个元素开始,每两个相邻元素必定构成一个通话记录,输出比较繁琐,需要谨慎处理。
关于日期的计算,要考虑全面,比如电话跨天,因此在考虑时间的时候应该把天、小时、分钟全部加起来,计算为分钟之和,既方便了排序,在计算价格的时候也可以方便的处理,计算价格采用的方法是以月初为基准,分别计算一条通话记录中off和on的话费然后做差。
下面的算法是在sunbaigui博客上直接摘录的,我在理解的基础上加了些注释便于理解,在这里感谢sunbaigui提供的众多优良解法。
#include<iostream>
using namespace std;
#include<vector>
#include<string>
#include<algorithm>
#include<stdio.h> struct Call
{
string name;
int month;
int date;
int hour;
int minute;
int total; // 总时间是对date、hour、minute之和换算为分钟,便于排序和时间的运算。
string status;
}; int charge[24];
vector<Call> all_calls; // 输入的原始记录
vector<Call> format_calls; // 对输入记录处理之后 bool compare(Call a,Call b) // 对sort函数指定比较规则,首先是名字按照字典顺序排序,如果名字相同则按时间顺序
{
if(a.name < b.name)
return 1;
else if(a.name == b.name && a.total < b.total)
return 1;
else
return 0;
} //calculate money from the begin of month to the time given
int chargeByTime(int time) // 对给定的分钟数相对于月初计算费用,off和on的费用差即为一条通话记录的话费。
{
int hours = time/60;
int minutes = time%60;
int money = 0;
int i;
for(i = 0;i<hours;i++)
money += charge[i%24]*60;
money += charge[i%24]*minutes;
return money;
} double calCost(Call s,Call t) // 计算话费的方法为off相对月初的话费减去on相对月初的话费
{
return (double)(chargeByTime(t.total)-chargeByTime(s.total))/100;
} int calLast(Call s,Call t) // 计算一条通话记录的时长
{
return (t.date-s.date)*24*60+(t.hour-s.hour)*60+(t.minute-s.minute);
} int main()
{
for(int i = 0;i<24;i++)
cin>>charge[i];
int N;
cin>>N;
while(N--)
{
Call c;
cin>>c.name;
cin>>c.month;
getchar(); // 注意getchar printf等是在头文件stdio.h中声明的。
cin>>c.date;
getchar();
cin>>c.hour;
getchar();
cin>>c.minute;
c.total = c.minute + 60*c.hour + 24*60*c.date;
cin>>c.status;
all_calls.push_back(c);
}
sort(all_calls.begin(),all_calls.end(),compare); // sort函数在algorithm.h中声明 // filter delete those bad record 过滤无用的通话记录
bool haveonline = false; // 用于判断是否上一条是on,从而判断应该丢弃上一条还是保留。
string curname; // 用于存储上一条记录的名字,如果这一条是off,并且名字和上一条一致,才能构成一条通话记录
for(int i=0;i<all_calls.size();i++)
{
if(haveonline == false && all_calls[i].status =="on-line" ) // 第一次遇到on,直接压入
{
format_calls.push_back(all_calls[i]);
haveonline = true;
curname = all_calls[i].name;
}
else if(haveonline == true && all_calls[i].status =="on-line") // 第二次遇到on,说明上一个on是无效的,先弹出上一条,再压入这一条。
{
format_calls.pop_back();
format_calls.push_back(all_calls[i]);
haveonline = true;
curname = all_calls[i].name;
}
else if(haveonline == true && all_calls[i].status =="off-line"&&all_calls[i].name ==curname) // 匹配成功的通话记录,压入off。
{
format_calls.push_back(all_calls[i]);
haveonline = false;
}
}
//the last must be offline
if((*(format_calls.end()-1)).status == "on-line") // 上面无法处理最后一条为on的情况,因此检测最后一条是否是on,是直接丢弃。
format_calls.pop_back(); //output
double totalcost = 0; // 用于累加总话费
curname = ""; // 用于判断是否应该输出总话费(总话费在一个人输出完毕后输出,第一次不输出,用curname是否为空来区分)
for(int i=0;i<format_calls.size();i+=2)
{ if(format_calls[i].name != curname)
{
if(curname!="")
{
printf("Total amount: $%.2f\n",totalcost);
totalcost = 0;
printf("%s %02d\n",format_calls[i].name.c_str(),format_calls[i].month);
}
else
{
printf("%s %02d\n",format_calls[i].name.c_str(),format_calls[i].month);
}
curname = format_calls[i].name;
}
printf("%02d:%02d:%02d",format_calls[i].date,format_calls[i].hour,format_calls[i].minute);
printf(" ");
printf("%02d:%02d:%02d",format_calls[i+1].date,format_calls[i+1].hour,format_calls[i+1].minute);
printf(" ");
printf("%d",calLast(format_calls[i],format_calls[i+1]));
printf(" ");
printf("$%.2f\n",calCost(format_calls[i],format_calls[i+1]));
totalcost+=calCost(format_calls[i],format_calls[i+1]);
}
printf("Total amount: $%.2f\n",totalcost); }