这道题就是乍一看很麻烦,其实稍微复杂一点的地方只是统计花费金额上,采用《算法笔记》上计算两日期差值的算法套过来就可以计算金额了,不过确实理解题意以及如何处理on-line和off-line匹配及舍入关系和理解话费计算还是挺花时间的。
总结一下:
- 首先定义结构体存储这些订单信息,按照字典序输出人名很简单,无非就是按照字典序排序后再挨个处理;
- on-line和off-line的匹配问题, 其实把时间按照先后排序之后,匹配关系及舍掉哪些订单就很显然了,将时间戳存储为字符串,就可以直接用string按照字典序排序就好了
综上两步,写一个cmp函数分别按照人名(第一顺序)和时间(第二顺序)的字典序来排序结构体数组即可得到下图所示效果。
- 花费金额计算问题
(1美元等于100美分)定义cost数组,cost[i]存第i到i+1小时单位分钟的花费;找到一对配对的消费记录后,记on为on-line对应记录,off为off-line对应记录,则on时间先于off,即天小时分钟至少有一个变量小于off,从小单位分钟开始累加(每次累加对应小时段的单位话费),分钟到60进位,小时到24进位(一开始手误也写成了60),直到和off时间相等。
这就是类似计算日期差值的算法,代码如下:
while(on.d<off.d || on.h<off.h || on.m < off.m ){
res+=cost[on.h];//
on.m++;
if(on.m==60){
on.m=0;
on.h+=1;
if(on.h==24){
on.h=0;
on.d+=1;
}
}
}
上述计算方式可以优化,即让on先追上off的天和小时数各-1,再从分钟还是逐步增加1,但是追小时的时候,情况比较复杂,有可能末尾处没有跨过60min,也就不能直接计算,因此稳妥起见还是暴力求解。(下述代码只优化了天)
while(on.d<off.d-1){//先追上天
res+=60*cost[24];//cost[24]是cost[0-23]之和
on.d++;
}
/*while(on.h<off.h-1){//没这么简单 还是不优化了
//再追小时,需用下一小时的话费单价
res+=60*cost[on.h+1];//cost[i]就表示区间i到i+1小时的单位电话费了
on.h++;
}*/
while(on.d<off.d || on.h<off.h || on.m < off.m ){
res+=cost[on.h];//
on.m++;
if(on.m==60){
on.m=0;
on.h+=1;
if(on.h==24){
on.h=0;
on.d+=1;
}
}
}
计算一个人的总通话分钟时,可以利用上述方法再开一个变量记录分钟一共累加了多少次,也可以让on和off分别以00:00:00(天:小时:分钟)为起点计算分钟差值,那么这俩的差值就是两者相差的分钟数,如02:10:44与00:00:00差值就是(2*24+10)*60+44,这对于不同时间段单位花费相同时计算及代码量较小。
4. 最后遇到的坑就是total等于0了(测试点2和3过不去,丢8分,太狠了),其实一开始自己写代码的时候加上了total不为零才输出账单,但是因为题目中有一句保证至少一个call配对了,就以为对于每个人都至少有一对call,所以total不为0,就把if中total!=0删掉了,但是事实上它表达的是N个record至少有一对call是配对的,因此可能某个人没有call配对,也就没有总金额,单纯通过i==N-1 || records[i].name!=records[i+1].name停止累计并输出total就会导致那些没有配对call的人也会输出出来。
如提交到牛客下例不通过:
72 53 94 95 71 82 31 1 97 36 8 94 44 22 3 51 58 72 27 57 7 39 55 80 38 CS07 07:11:04:05 off-line CS05 07:15:19:37 on-line CS09 07:26:10:05 on-line CS00 07:29:08:20 off-line CS00 07:27:16:05 on-line CS06 07:25:13:50 off-line CS07 07:17:08:03 on-line CS04 07:25:02:50 on-line CS02 07:05:09:47 on-line CS03 07:23:18:20 on-line CS05 07:30:05:51 on-line CS06 07:08:11:57 off-line CS05 07:13:18:12 on-line CS07 07:21:07:52 off-line CS08 07:14:12:34 off-line CS04 07:21:18:49 off-line CS00 07:31:11:35 on-line CS09 07:03:10:58 on-line CS08 07:17:01:46 off-line CS06 07:02:02:30 off-line CS07 07:16:08:51 on-line CS02 07:29:17:32 off-line CS03 07:14:19:43 on-line CS05 07:12:06:43 off-line CS09 07:25:04:45 off-line CS04 07:23:15:19 on-line CS08 07:03:10:42 on-line CS03 07:01:12:02 off-line CS07 07:30:07:23 off-line CS03 07:28:14:33 off-line CS00 07:08:18:33 off-line CS08 07:13:13:44 off-line CS02 07:30:13:47 on-line CS06 07:14:21:54 on-line CS00 07:09:21:00 on-line CS02 07:24:12:24 off-line CS09 07:24:19:24 off-line CS03 07:16:02:33 on-line
这个点其实也告诉自己,严谨的条件该加还是加上,避免自己理解错误带来的bug,否则真的很难找到是因为多输出而出错。
·
·
·
·
·
·
AC代码:
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int cost[25]={0};//cost[24]记录总和
typedef long long LL;
struct Record{
string name;
string time;
string status;
Record(string _name,string _time,string _status){
name=_name;
time=_time;
status=_status;
}
Record(){
name=time=status="";
}
}records[1001];
struct Time{
int d,h,m;//天小时分钟
};
bool cmp(struct Record a,struct Record b){
if(a.name!=b.name) return a.name < b.name;
else return a.time < b.time;
}
double countMoney(string begin,string end);
int main() {
for(int i=0;i<24;i++){
cin >> cost[i];
cost[24]+=cost[i];
}
int N;
cin >> N;
map<string,bool> mp;
for(int i=0;i<N;i++){
string name,time,status;
cin >> name >> time >> status;
records[i]=Record(name,time,status);
mp[name]=true;//第一次输出这个人的账单时为true
}
sort(records,records+N,cmp);//时间也用string存储,排序直接按照字典序拍就好
string cur="";
double total=0;
for(int i=0;i<N;i++){
if(records[i].status=="on-line"){
if(i+1<N && records[i].name==records[i+1].name && records[i+1].status=="off-line")//下一个还有记录,同一个人,下一个是offline
{
//处理钱财
if(mp[records[i].name]){//先看是否需要输出前置信息
mp[records[i].name]=false;
cout << records[i].name << " " << records[i].time[0] << records[i].time[1] << endl;
}
struct Record on,off;
on=records[i];
off=records[i+1];
cout << on.time.substr(3) << " " << off.time.substr(3) << " ";
int begin= (((on.time[3]-'0')*10+on.time[4]-'0')*24+(on.time[6]-'0')*10+on.time[7]-'0')*60+(on.time[9]-'0')*10+on.time[10]-'0';
int end=(((off.time[3]-'0')*10+off.time[4]-'0')*24+(off.time[6]-'0')*10+off.time[7]-'0')*60+(off.time[9]-'0')*10+off.time[10]-'0';
cout << end-begin << " ";
//计算金额
double tmp=countMoney(on.time,off.time);
printf("$%.2f",tmp);
total+=tmp;
cout << endl;
//i++;//因为已经处理过offline了,所以要多自增一下,因为前面if了on-line,所以不自增也可
}
}
if((i+1==N || records[i].name!=records[i+1].name) && total!=0 ){
/*if(i+1!=N)
cout << records[i].name << " " << records[i+1].name << endl;*/
printf("Total amount: $%.2f\n",total);
total=0;
}
}
/*for(int i=0;i<N;i++){
cout << records[i].name << " " << records[i].time << " " << records[i].status << endl;
}*/
return 0;
}
double countMoney(string begin,string end){
struct Time on,off;
on.d=(begin[3]-'0')*10+(begin[4]-'0');
on.h=(begin[6]-'0')*10+(begin[7]-'0');
on.m=(begin[9]-'0')*10+(begin[10]-'0');
off.d=(end[3]-'0')*10+(end[4]-'0');
off.h=(end[6]-'0')*10+(end[7]-'0');
off.m=(end[9]-'0')*10+(end[10]-'0');
double res=0;
while(on.d<off.d-1){//先追上天
res+=60*cost[24];//cost[24]是cost[0-23]之和
on.d++;
}
/*while(on.h<off.h-1){//没这么简单 还是不优化了
//再追小时,需用下一小时的话费单价
res+=60*cost[on.h+1];//cost[i]就表示区间i到i+1小时的单位电话费了
on.h++;
}*/
while(on.d<off.d || on.h<off.h || on.m < off.m ){
res+=cost[on.h];//
on.m++;
if(on.m==60){
on.m=0;
on.h+=1;
if(on.h==24){
on.h=0;
on.d+=1;
}
}
}
return res*1.0/100;
}