PAT甲级真题 1016 Phone Bills (25分) C++实现(计算时间格式差值,按分钟计费的方法)

题目

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

思路

很麻烦的一道题,其实可以拆分成两道小题:

找配对的通话记录

为顾客的通话记录建立结构体,三部分信息均用string存储。对该结构体数组,根据字符串比较排序:姓名优先,时间次之。

接下来,只要找连续出现且姓名相同的一对on-line、off-line即可。

若遇到新的名字,则结算上个顾客的总费用,打印新顾客名字、月份,并将总费用归零。

计算每对通话记录相隔时长

已经得到格式如02:00:01 04:23:59的一对时间,怎么求其计费呢?

先将字符串转为数字,使用stringstream将dd、hh、mm读进整型里:

int d, h, m;
char ch;  //用于保存:
stringstream ss(s);
ss >> d >> ch >> h >> ch >> m;

再计算出相隔的总分钟数,模拟减法的借位相减。

最后逐分钟累加计费,以总分钟数为循环结束条件。为了提高效率,单独计算相隔的整天数及费用。

本文方法直观,但笨。柳神的方法是,找基准点。求两个时间各自相对00:00:00的消费,其差值即为期间消费。聪明,手动👍。

柳神代码:1016. Phone Bills (25)-PAT甲级真题

出现的错误

一开始出现了段错误,访问数组越界了。

正确代码:

    while (i<n){
        while (i<n && R[i].state=="off-line"){
            i++;
        }
        if (i>=n-1 || R[i+1].name!=R[i].name || R[i+1].state=="on-line"){
            i++;
            continue;
        }
        string name = R[i].name;
    	....
    }

错误代码1:

    while (i<n){
        while (R[i].state=="off-line"){  //此处可能越界
            i++;
        }
        if (i>=n-1 || R[i+1].name!=R[i].name || R[i+1].state=="on-line"){  //无配对
            i++;
            continue;
        }
        string name = R[i].name;
    	....
    }

错误代码2:

    while (i<n){
        while (i<n && R[i].state=="off-line"){
            i++;
        }
        string name = R[i].name;  //此处可能越界
        if (i>=n-1 || R[i+1].name!=R[i].name || R[i+1].state=="on-line"){
            i++;
            continue;
        }
    	....
    }

代码

#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>
#include <algorithm>
using namespace std;

struct record{
    string name;
    string time;
    string state;
};
bool cmp(record r1, record r2){
    return (r1.name==r2.name) ? (r1.time<r2.time) : (r1.name<r2.name);
}
int main(){
    vector<int> T(24);
    int dayToll = 0;
    for (int i=0; i<24; i++){
        cin >> T[i];
        dayToll += T[i];
    }
    dayToll *= 60;
    int n;
    cin >> n;
    vector<record> R(n);
    for (int i=0; i<n; i++){
        cin >> R[i].name >> R[i].time >> R[i].state;
    }
    string month = R[0].time.substr(0, 2);
    sort(R.begin(), R.end(), cmp);
     
    int i = 0;
    string preName = "";
    float sum = 0.0;
    while (i<n){
        while (i<n && R[i].state=="off-line"){  //定位到第一个on-line
            i++;
        }
        if (i>=n-1 || R[i+1].name!=R[i].name || R[i+1].state=="on-line"){  //无配对
            i++;
            continue;
        }
        string name = R[i].name;
        if (name != preName){  //新找到一个有账单的用户
            if (sum > 0){  //结算上个顾客的总费用
                cout << "Total amount: $" << setprecision(2) << fixed << sum << endl;
            }
            cout << name << " " << month << endl;
            preName = name;
            sum = 0.0;  //累计消费归0
        }
        string s[2];
        s[0] = R[i].time.substr(3, 8);  //开始时间
        s[1] = R[i+1].time.substr(3, 8);  //结束时间
        int d[2], h[2], m[2];
        for (int j=0; j<2; j++){
            stringstream ss(s[j]);
            char ch;
            ss >> d[j] >> ch >> h[j] >> ch >> m[j];
        }
        if (m[1] < m[0]){
            m[1] += 60;
            h[1]--;
        }
        if (h[1] < h[0]){
            h[1] += 24;
            d[1]--;
        }
        float cost = (d[1] - d[0]) * dayToll;  //差的整天数
        int duration = (h[1] - h[0]) * 60 + (m[1] - m[0]);  //差的分钟数
        int curToll = T[h[0]];
        for (int j=0; j<duration; j++){
            cost += curToll;
            m[0]++;
            if (m[0]==60){
                m[0] = 0;
                h[0] = (h[0] + 1) % 24;
                curToll = T[h[0]];
            }
        }
        cost /= 100;
        sum += cost;
        duration += (d[1] - d[0]) * 1440; 
        cout << s[0] << " " << s[1] << " " << duration << " $" << setprecision(2) << fixed << cost << endl;
        i += 2;
    }
    if (sum > 0){  //打印最后一名顾客总费用
        cout << "Total amount: $" << setprecision(2) << fixed << sum << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值