1. 程式人生 > >【PAT甲級】1016 Phone Bills

【PAT甲級】1016 Phone Bills

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

題意分析

這題的輸入是先給出24小時每個小時的話費每分鐘話費,然後給出n,隨後附上n條通話記錄,每條通話記錄內容有顧客姓名,記錄時間,記錄型別,記錄型別有上線記錄和下線記錄。

題目的輸出要求給出每個顧客的有效通話記錄,每條通話記錄的開始和結束時間,每條通話記錄的單獨話費和最後的總花費。

個人思路

這題其實還是對排序演算法的應用,有幾個關鍵點如下所示。

1、要對通話記錄進行排序,要實現先按字母序排序,後按通話時間從小到大排序,因此要自己寫出一個比較函式。

2、要計算出每條通話記錄從上線到下線的時間段內的總話費,每個小時的每分鐘話費可能都不同。我是在一個迴圈中模擬時間的增長,每個小時單獨計算增加的話費,同時將開始時間增加,直到兩個時間相等時退出迴圈。

3、要判斷哪些通話記錄是有效的,要注意:如果上條記錄是上線的,下條記錄如果是上線則替換上條記錄的上線時間,下條記錄如果是下線的則進行話費計算;如果上條記錄是下線的,下條記錄如果是下線的則直接忽略,下條記錄如果是上線的則開始新的一條記錄。

4、本題最大的坑:輸出時只輸出話費大於0的顧客,如果話費為0,連名字都不輸出。

程式碼實現

#include <cstdio>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <vector>
#include <cmath>
#include <algorithm>
#include <iostream>
#define ll long long
#define eps 1e-8
#define INF 0x7FFFFFFF

using namespace std;

// 24小時話費
int cents_per_min[24] = {0};

// 時間結構體
struct CallTime {
    int month, day, hour, minute;
};

// 通話記錄結構體
struct CallRecord {
    string name;
    CallTime call_time;
    bool online; // 記錄型別 1上線 0下線
};

bool cmp(CallRecord r1, CallRecord r2) {
    if (r1.name == r2.name) {
        // return r1.call_time < r2.call_time
        if (r1.call_time.day < r2.call_time.day) return true;
        else if(r1.call_time.day > r2.call_time.day)return false;
        
        if (r1.call_time.hour < r2.call_time.hour) return true;
        else if(r1.call_time.hour > r2.call_time.hour) return false;
        
        if (r1.call_time.minute < r2.call_time.minute) return true;
        else if(r1.call_time.minute > r2.call_time.minute)return false;
    }
    return r1.name < r2.name;
}

// 求出兩個時間的分鐘差
int time_sub(CallTime t1, CallTime t2) {
    int min1, min2;
    min1 = t1.day*24*60 + t1.hour*60 + t1.minute;
    min2 = t2.day*24*60 + t2.hour*60 + t2.minute;
    return abs(min1-min2);
}

// 求出某個電話記錄的話費
int phone_cost(CallTime t1, CallTime t2) {
    int ret = 0;
    while (!(t1.day == t2.day && t1.hour == t2.hour && t1.minute == t2.minute)) {
        if (t1.day == t2.day && t1.hour == t2.hour) {
            ret += cents_per_min[t1.hour]*(t2.minute-t1.minute);
            t1.minute = t2.minute;
        }
        else {
            ret += cents_per_min[t1.hour]*(60-t1.minute);
            t1.minute = 0;
            if (t1.hour == 23) {
                t1.hour = 0;
                t1.day ++;
            }
            else {
                t1.hour ++;
            }
        }
    }
    return ret;
}

int main() {
    // 輸入cents per minute
    for (int i = 0; i < 24; i ++) {
        cin >> cents_per_min[i];
    }
    // 輸入記錄個數
    int n;
    cin >> n;
    // 對每條記錄進行儲存
    vector<CallRecord> records;
    for (int i = 0; i < n; i ++) {
        // 將輸入資訊先存放到record裡
        CallRecord record;
        string name, call_time, type;
        cin >> name >> call_time >> type;
        record.name = name;
        const char *s = call_time.data();
        sscanf(s, "%d:%d:%d:%d", &record.call_time.month, &record.call_time.day, &record.call_time.hour, &record.call_time.minute);
        if (type == "on-line") record.online = true;
        else record.online = false;
        // 將記錄存入vector
        records.push_back(record);
    }
    
    // 按照先字母表,後時間前後進行排序
    sort(records.begin(), records.end(), cmp);
    
    int sum = 0; // 記錄總開銷
    bool last_online = false; // 記錄上一條記錄是否是online
    CallTime begin_time, end_time; // 一條通話記錄的開始和結束時間
    map <string, int> idx; // 建立顧客姓名和姓名編號的對映
    int name_cnt = 0; // 顧客姓名數量
    for (int i = 0; i < n; i ++) {
        CallRecord record = records[i];
        // 如果是上線記錄,則記錄開始電話的時間
        if (record.online) {
            begin_time = record.call_time;
            last_online = true;
        }
        // 如果是下線記錄,且上條記錄是上線記錄
        else if (last_online && !record.online) {
            end_time = record.call_time;
            last_online = false;
            // 計算分鐘數和花費數
            int minutes = time_sub(begin_time, end_time);
            int rec_cost = phone_cost(begin_time, end_time);
            sum += rec_cost;
            // 第一次時列印名字
            if(idx[record.name] == 0) {
                idx[record.name] = ++name_cnt;
                cout << record.name << " ";
                printf("%02d\n", record.call_time.month);
            }
            // 列印通話記錄
            printf("%02d:%02d:%02d ", begin_time.day, begin_time.hour, begin_time.minute);
            printf("%02d:%02d:%02d ",end_time.day, end_time.hour, end_time.minute);
            printf("%d $%.2lf\n", minutes, 1.0*rec_cost/100);
        }
        
        bool customer_end = false;
        if (i == n-1) customer_end = true;
        else if (record.name != records[i+1].name) customer_end = true;
        if (customer_end) {
            if (sum != 0) printf("Total amount: $%.2lf\n", 1.0*sum/100);
            sum = 0;
            last_online = false;
        }
        
    }
    return 0;
}

總結

學習不息,繼續加油

PAT奇奇怪怪的坑實在是太多了,審題真的很重要。