PAT-A 1016 Phone Bills (25 分)

1016 Phone Bills (25 分)

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

分析:

题目中没有给出的信息:输出时,如果某个用户没有匹配到通话记录,那么这个用户的信息不能输出。如果输出了姓名月份和总费用0的信息,则会卡两个点(在这里卡了好久,哭)。

要解决的问题:

1.如何匹配出呼叫(on-line)和挂断(off-line):将每个用户各自的通话时间按时间升序排列后,可以匹配的通话记录一定是相邻的的on-line和off-line。比如,如果对通话时间排序后,时间的状态为on on off on on off on on on,那么可以匹配到紫色的两对通话记录。

2.如何计算通话时长和资费,给定两个时间(题目保证所有通话在一同个月份内)day:hour:minute,可以采用一直自增开始时间的mintute,如果minute满60,就进位,hour满24进位,直到小的日期等于大的日期。也可以像我的代码中那样采取判断(自认为采用判断会快一些)。

数据结构:使用map<string , vector<string>>来保存输入的数据,其中map的string类型的关键字存储顾客的姓名,每个顾客对应一个vector<string>,里面存储输入的每各通话记录的时间和通话记录的状态。比如,如果输入了

“CYLL 01:01:06:01 on-line”和“CYLL 01:01:08:03 off-line”

那么关键字“CYLL”对应的vector<string>中将有两个元素,“01:01:06:01 on-line”和“01:01:08:03 off-line”。这样,只需要按照字典序对vector中的字符串进行排序,便可以将顾客的通话记录按时间升序排列,因为一个用户不可有两个相同的通话时间。这样方便匹配通话。

另一个map<string, vector<pair<string, string>>来保存每个顾客匹配到的通话记录。关键字string同样是顾客的姓名,vector中的每个元素是pair<strrng, string>类型,pair的第一个string用来存储开始通话(on-line)的时间,第二个string用来存储结束通话的时间(off-line)。

使用map的好处是可以自动按照字典序将用户信息按照字典序降序排列,但是数据结构嵌套太多,大大增加了编码复杂度。

#include <cstdio>
#include <vector>
#include <map>
#include <algorithm>
#include <string>
#include <iostream>

using namespace std;

bool cmp(string s1, string s2)
{   //按字典序递增为字符串排序
    return s1 < s2;
}

/*
hour1:minute1为通话开始时间,hour2:minute2为通话结束时间
通话发生在一天内
计算通话的分钟数和费用,单位是美元
printf("%02d:%02d:%02d %02d:%02d:%02d %d $%.2f", day1, hour1, minute1, day2, hour2, minute2, time, single);
*/
pair<int, double> getTimeAndFeeInOneDay(int *rate, int hour1, int minute1, int hour2, int minute2)
{
    int time = 0;
    double single = 0;
    if(hour1 == hour2)//通话发生在一个小时内,比如02:11到02:30
    {
        time = minute2 - minute1;
        single = time * rate[hour1] / 100.0;//100美元为1美分
    }
    else//通话跨越了至少一个小时,比如02:50到04:10
    {
        //先计算从开始通话到该小时结束,如果2:50开始通话,计算2:50到3:00的时长
        single = (60 - minute1) * rate[hour1] / 100.0;
        time = 60 - minute1;
        //再计算通话结束时间所在的小时,比如04:10结束,计算从04:00到04:10
        single += minute2 * rate[hour2] / 100.0;
        time += minute2;
        //再计算中间跨越的小时,比如02:50到4:10需要计算3点到4点
        while(++hour1 != hour2)
        {
            single += 60.0 * rate[hour1] / 100.0;
            time += 60;
        }
    }
    return {time, single};
}

int main()
{
    int rate[24];
    for(int i = 0; i < 24; i++)
        scanf("%d", rate + i);//保存不同时段的费率

    int n;
    scanf("%d", &n);
    map<string, vector<string>> info;//保存输入的姓名和童话信息
    map<string, vector<pair<string, string>>> pairs;//保存每个人匹配到的通话记录
    char name[21];//顾客姓名不超过20个字符
    char time[13];//off-line或on-line的长度是固定的
    char state[10];//off-line有8个字符
    for(int i = 0; i < n; i++)
    {
        scanf("%s %s %s", name, time, state);
        string c(name);//customer
        string t(time);
        string s(state);
        if(info.find(c) == info.end())
        {
            info[c] = {t + " " + s};//将当前顾客的通话记录向量初始化为第一条通话记录
            pairs[c] = {};//为该顾客匹配的通话记录列表建立vector,初始化为空
        }
        else
            info[c].push_back(t + " " + s);//追加当前顾客的通话记录到通话记录向量中
    }
    //对每个顾客的通话时间进行匹配,匹配的结果保存到pairs中
    for(auto it = info.begin(); it != info.end(); it++)//遍历每一个顾客
    {
        vector<string>& temp = it->second;//声明对顾客的通话记录的引用,书写方便
        sort(temp.begin(), temp.end(), cmp);//对每个顾客的通话记录按照通话时间进行从小到大排序
        for(int i = 0; i < temp.size(); i++)
        {   //遍历该顾客的通话记录
            string& s = temp[i];//声明对当前顾客的当前通话记录的引用
            if(i > 0 && s[s.size() - 6] == 'f')//该条通话记录为off-line,i>0防止出现一个顾客第一个通话记录是off-line的情况
            {
                string& s2 = temp[i - 1];
                char c = s2[s2.size() - 6];//查看前一个通话记录的状态
                //printf("test:%c\n", c);
                if(c == 'n')//前一个通话记录的状态是on-line,匹配成功,注意s是通话结束时间s2是通话开始时间
                {   //去掉通话状态,将匹配的通话记录构造pair加入到该顾客的通话列表中
                    pairs[it->first].push_back({s2.substr(0, 11), s.substr(0, 11)});
                }
            }
        }
    }
    //计算每个顾客的账单并打印
    int month;//记录账单的月份
    int day1, hour1, minute1;//保存匹配到的通话开始时间的信息
    int day2, hour2, minute2;//保存匹配到的通话结束时间的信息
    double single;//每通电话的资费
    double total;//每个用户的月度总账单
    int lasting;//每通电话通话时间
    sscanf(time, "%d", &month);//获取账单的月份
    for(auto it = pairs.begin(); it != pairs.end(); it++)
    {
        total = 0.0;//每个顾客账单总总额初始化为0
        vector<pair<string, string>>& temp = it->second;//声明引用,方便书写
        if(temp.empty())
            continue;//没有匹配到通话的顾客信息不打印
        printf("%s %02d\n", it->first.c_str(), month);//顾客有匹配到的通话,打印信息
        for(int i = 0; i < temp.size(); i++)//遍历该顾客的所有通话
        {
            single = 0.0;//每通通话费用
            lasting = 0;//每通通话时长,单位为分钟
            string& on = temp[i].first;//开始通话的时间
            string& off = temp[i].second;//结束通话的时间
            //month只起到占位符的作用
            sscanf(on.c_str(), "%d:%d:%d:%d", &month, &day1, &hour1, &minute1);
            sscanf(off.c_str(), "%d:%d:%d:%d", &month, &day2, &hour2, &minute2);
            if(day1 == day2)//通话发生在一天内
            {
                pair<int, double> temp = getTimeAndFeeInOneDay(rate, hour1, minute1, hour2, minute2);
                lasting = temp.first;
                single = temp.second;
            }
            else
            {
                //先计算从开始打电话到当天结束时的时间和资费
                pair<int, double> temp1 = getTimeAndFeeInOneDay(rate, hour1, minute1, 24, 0);
                //再计算从挂断电话的那天0点0分到挂断电话时的时间和资费
                pair<int, double> temp2 = getTimeAndFeeInOneDay(rate, 0, 0, hour2, minute2);
                lasting = temp1.first + temp2.first;
                single = temp1.second + temp2.second;
                //再计算中间可能打了超过1天的时间,比如1号打的电话,4号才挂断,
                //中间有2天完整的通话时间(真的丧心病狂)
                int t = day1;//后面要输出day1,所以不能直接在day1上自增
                while(++t != day2)
                {
                    lasting += 60 * 24;
                    for(int i = 0; i < 24; i++)
                        single += rate[i] * 60.0 / 100.0;
                }
            }
            total += single;
            printf("%02d:%02d:%02d %02d:%02d:%02d %d $%.2f\n", day1, hour1, minute1, day2, hour2, minute2, lasting, single);
        }
        printf("Total amount: $%.2f\n", total);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38127801/article/details/86441224
今日推荐