PAT_1016 Phone Bills (25 分)

题目链接
在这里插入图片描述
在这里插入图片描述

这道题就是乍一看很麻烦,其实稍微复杂一点的地方只是统计花费金额上,采用《算法笔记》上计算两日期差值的算法套过来就可以计算金额了,不过确实理解题意以及如何处理on-line和off-line匹配及舍入关系和理解话费计算还是挺花时间的。

总结一下:

  1. 首先定义结构体存储这些订单信息,按照字典序输出人名很简单,无非就是按照字典序排序后再挨个处理;
  2. on-line和off-line的匹配问题, 其实把时间按照先后排序之后,匹配关系及舍掉哪些订单就很显然了,将时间戳存储为字符串,就可以直接用string按照字典序排序就好了
    综上两步,写一个cmp函数分别按照人名(第一顺序)和时间(第二顺序)的字典序来排序结构体数组即可得到下图所示效果。
    在这里插入图片描述
  3. 花费金额计算问题
    (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;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值