PAT_A1016 | Phone Bills

1016 Phone Bills (25point(s))

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

 


本题的主要思路

1、根据输入的通话记录,存入对应的结构体数组中。

2、然后按照要求排序,使得结构体数组按照名字、时间排序。

3、最后对有合理通话记录的人,输出账单:

         姓名、月份 

         每条合理的通话记录... (需要计算单次通话时间、费用

         总消费额


每条通话信息按照结构体储存,最终总和存入一个结构体数组内:

struct node {
    char name[25];
    int month, day, hour, minute;
    bool isOn;
} a[1005];

1、正确处理输入 

对于首先输入的24个数字,分别对应 0 ~ 23 时中,每分钟电话费的价格(以cent为单位)。可以使用数组储存,分别对应 0 ~ 23 的下标由于最终的输出是以dollar为单位,在此进行单位换算,再存入。换算单位后会出现小数,所以数组应该为float。

    float money[24], total_day = 0;
    for (int i = 0; i < 24; i++) {
        int cent;
        scanf("%d", &cent);
        money[i] = cent / 100.0;   //将cent换算成dollar
        total_day += money[i] * 60;
    }

然后依次处理输入,正确地存入结构体数组中:

int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        char str[9];
        scanf("%s %d:%d:%d:%d %s", a[i].name, &a[i].month, &a[i].day, &a[i].hour, &a[i].minute, str);
        a[i].isOn = str[2] == '-';
    }

 

2、合理排序

按照题目意思,为了之后处理翻遍。应该按照名字字典序排序,对于名字一样的通话记录,应该按照时间先后排序。

对该结构体数组排序,可以直接使用c++"algorithm"库中的sort函数,具体如何使用可以看看这篇文章:排序算法 | sort函数的使用

对应的cmp函数如下:

bool cmp(struct node x, struct node y) {
    if (strcmp(x.name, y.name) != 0)
        return strcmp(x.name, y.name) < 0;
    else if (x.day != y.day)
        return x.day < y.day;
    else if (x.hour != y.hour)
        return x.hour < y.hour;
    else
        return x.minute < y.minute;
}

然后直接调用同排序即可:

 sort(a, a + n, cmp);

 

3、输出每个人的账单

我们已经按照姓名排序好了,那么每个人的通话记录是按照时间顺序在数组内的。我们可以一个一个人来考虑。

那么怎么判断每个人账单的分界点呢?(也就是什么时候这个人的通话记录已经被我们计算完了,需要计算下一个人了)可以用一个字符串name来储存当前账单人的名字,每次在开始计算时,要判断下一个数据中的名字是否和name完全一样。


账单应该包括:

         姓名、月份 

         每条合理的通话记录... (需要计算单次通话时间、费用

         总消费额

如何计算需要输出的东西?

1、首先要找到合理的通话记录

什么是合理的通话记录:按照题目要求,经过我们排序后。on-line和off-line 前后相邻的两条才算有效,那么在循环访问每一条记录时,要按照这个规则确认是有效的通话记录才接着计算,否则继续访问下一条。

2、计算

找到合理的通话记录,即代表我们要计算当前记录和下一条记录之间的费用和时间差。费用和时间差可以同时计算,计算时分清楚时间关系去计算更清晰:

分别考虑两条通话记录是否在同一天

🔺同一天

     🌂同一小时 :

  •   直接计算第二条减第一条的时间部分

     🌂非同一小时 :

  •   先计算第一条在该小时内剩余的部分
  •   再计算后一条在该小时内经过的部分
  •   最后计算两条之间隔着的完整小时的部分

🔺非同一天

  •   先计算第一条在该小时内剩余的部分
  •   计算这天中第一条该小时后剩余的完整小时的部分
  •   再计算后一条在该小时内经过的部分
  •   计算这天中后一条该小时前经历的完整小时的部分
  •   最后计算两条之间隔着的完整天的部分

 

需要注意的几个点:

  1. 在一个账单中,计算完每条合理的通话记录后,要更新整个账单的时间和费用
  2. 是否需要输出账单,即要当有第一条合理的通话记录出现时才开始输出账单的第一行(姓名、月份 )
  3. 计算完一条合理的通话记录后就输出
  4. 一旦判断出账单的分界点,就将账单的最后一行输出(总消费额)

 

 

本题完整代码如下:

//
// Created by LittleCat on 2020/2/12.
//

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

struct node {
    char name[25];
    int month, day, hour, minute;
    bool isOn;
} a[1005];

bool cmp(struct node x, struct node y) {
    if (strcmp(x.name, y.name) != 0)
        return strcmp(x.name, y.name) < 0;
    else if (x.day != y.day)
        return x.day < y.day;
    else if (x.hour != y.hour)
        return x.hour < y.hour;
    else
        return x.minute < y.minute;
}

int main() {
    float money[24], total_day = 0;
    for (int i = 0; i < 24; i++) {
        int cent;
        scanf("%d", &cent);
        money[i] = cent / 100.0;   //将cent换算成dollar
        total_day += money[i] * 60;
    }

    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        char str[9];
        scanf("%s %d:%d:%d:%d %s", a[i].name, &a[i].month, &a[i].day, &a[i].hour, &a[i].minute, str);
        a[i].isOn = str[2] == '-';
    }
    sort(a, a + n, cmp);

    float total = 0;
    bool flag = false;
    char name[25] = {'\0'};
    for (int i = 0; i < n; i++) {

        if (strcmp(name, a[i].name) != 0 || i == n - 1) {

            if (total != 0)
                printf("Total amount: $%.2f\n", total);  //输出总费用

            if (i == n - 1)
                break;

            strcpy(name, a[i].name);
            total = 0;
            flag = false;
        }

        if(strcmp(a[i].name, a[i + 1].name) != 0)
            continue;
        if (!a[i].isOn)
            continue;
        if (a[i + 1].isOn)
            continue;

        /* 存在有效通话记录:保证当前是on下一个是off */
        float cost = 0;
        int m = 0;
        if (a[i].day == a[i + 1].day) {
            if (a[i].hour == a[i + 1].hour) {
                m = a[i + 1].minute - a[i].minute;
                cost = m * money[a[i].hour];
            } else {
                cost += (60 - a[i].minute) * money[a[i].hour];
                cost += a[i + 1].minute * money[a[i + 1].hour];
                m += 60 - a[i].minute + a[i + 1].minute;
                for (int j = a[i].hour + 1; j < a[i + 1].hour; j++) {
                    cost += 60 * money[j];
                    m += 60;
                }
            }
        } else {
            cost += (60 - a[i].minute) * money[a[i].hour];
            m += 60 - a[i].minute;
            for (int j = a[i].hour + 1; j < 24; j++) {
                cost += 60 * money[j];
                m += 60;
            }

            cost += a[i + 1].minute * money[a[i + 1].hour];
            m += a[i + 1].minute;
            for (int j = 0; j < a[i + 1].hour; j++) {
                cost += 60 * money[j];
                m += 60;
            }

            for (int j = a[i].day + 1; j < a[i + 1].day; j++) {
                cost += total_day;
                m += 24 * 60;
            }

        }

        if(!flag) {
            flag = true;
            printf("%s %02d\n", name, a[i].month);   //输出人名、月份
        }
        total += cost;
        printf("%02d:%02d:%02d %02d:%02d:%02d %d $%.2f\n", a[i].day, a[i].hour, a[i].minute,
               a[i + 1].day, a[i + 1].hour, a[i + 1].minute, m, cost);
    }

    return 0;
}

 

还可以改进的部分:(有点懒惰没有写代码了)

  • 计算时间那里的代码有点复杂,有一种代码简单但是效率低一点的方法:

可以考虑从第一个时间一分一分往后面推进直到等于第二个时间,当中每推进一分钟就计算一分钟即可。

为了不改变原始的时间数据,应该编写函数,传入参数后,改变形参计算即可。 



end 

欢迎关注个人公众号 鸡翅编程 ”,这里是认真且乖巧的码农一枚。

---- 做最乖巧的博客er,做最扎实的程序员 ----

旨在用心写好每一篇文章,平常会把笔记汇总成推送更新~

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值