电话账单

电话账单

1. 题目地址

    https://www.acwing.com/problem/content/1495/

2. 题目解析

    这道题的题意非常复杂,具体可以归结为:
        1.  需要接收各时间段的话费信息,这里可以使用数组接收
        2.  需要一个结构体来存储记录的信息:格式化时间(便于日后的输出),由时间所转换过来的分钟数(这里主要方便记录之间的排序和计算花费),该记录的状态。(用于匹配,判断该记录是否合法)
        3.  由于需要按照字典序输出该用户的账单信息。因此使用map来存储:key:用户名 value:该用户的通话记录.map是天然有序的,内部为红黑树。因此:key(用户名)自动按照字典序排列。
        4.  需要使用前缀和算法来预处理:某月100分开始到某分钟所需要的总花费。当需要求从S分钟开始到E分钟结束这一时间段的花费时,我们就可以使用公式:sum[E] - sum[S]; 代表:某月100分开始到E分钟所需要的总花费 - 某月100分开始到S分钟所需要的总花费。这样就求得了S分钟开始到E分钟所需要的总花费。
        5.  美分到美元需要进行转换:1美元 = 100美分。
        6.  总花费应为浮点数,并且需要保留两位小数。
        7.  需要熟练运用:sprintf,c_str,运算符重载,格式化输入等知识点。

3. 题解

    这里直接按照上述的解析做就行。唯一需要注意的就是:
        1.  细节。
        2.  前缀和算法。如果忘记,请参考之前博客。

4. 代码

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

using namespace std;

//代表最高的记录数
const int N = 1010;

//代表一个月内最多的分钟数
const int M = 31*1440 + 10;

//代表通话记录的结构体
struct Record{
    int minutes;           //代表该记录的分钟数(转换而来)
    string status;         //代表该记录的状态(在线/非在线)
    string format_time;    //代表标准时间格式
    
    //由于按照分钟从小到大排序,因此这里需要重载小于号
    bool operator<(const Record& c){
        return minutes < c.minutes;
    } 
};

//将用户和该用户的通话记录关联起来
//使用map来存储:key:用户名 value:该用户的通话记录
map<string,vector<struct Record>> persons;

//代表从某月1日0点0分开始到某分钟所需要的花费(需要进行预处理)
//采用前缀和思想
double sum[M];

//存储各个时间段的话费
int cost[24];

char name[20],status[20];
int month,day,hour,minute;


int main(){
    int n;
    //读入话费
    for(int i = 0;i < 24;i ++){
        scanf("%d",&cost[i]);
    }
    //预处理:从某月1日0点0分开始到某分钟所需要的花费
    //1.    这里注意:i - 1的原因就是:假设,i = 61, 那么第61分钟的花费实际上为:60 % 1440 / 60 = 1。
    //      因此,应该算作:1:00 - 2:00 的区间当中。
    //      再举一个例子:假设,i = 60, 那么第60分钟的花费实际上为:59 % 1440 / 60 = 0。
    //      因此,应该算作:0:00 - 1:00 的区间当中。
    //      如果这里为i,那么第60分钟的花费就算做1:00 - 2:00的区间当中了,这显然不正确。
    //2.    这里将花费/100的原因就是:将美分->美元
    for(int i = 1;i < M; i ++){
        sum[i] = sum[i-1] + cost[(i - 1) % 1440 / 60] / 100.0;
    }
    //读入记录
    scanf("%d",&n);
    for(int i = 0;i < n;i ++){
        //当读入信息时,采用scanf格式化读入
        scanf("%s %d:%d:%d:%d %s",name,&month,&day,&hour,&minute,status);
        //构造记录:
        //1.    得到格式化后的时间,采用sprintf函数来生成格式化后的字符串
        //不够的话,可用0补齐
        char format_time[20];
        sprintf(format_time,"%02d:%02d:%02d",day,hour,minute);
        //2.    得到该记录距离当月1日0点0分的分钟数
        int minutes = (day - 1) * 1440 + hour * 60 + minute;
        //3.    状态不用处理,直接放到vector数组即可
        persons[name].push_back({minutes,status,format_time});
    }
    //注意:map是天然有序的,内部为红黑树。因此:key(用户名)自动按照字典序排列。
    //这里不需要处理。
    //遍历map,输出信息
    for(auto person : persons){
        string name = person.first;
        auto records = person.second;
        //代表该用户所花费的总美元数
        double total = 0;
        //将记录按照时间(分钟数)从小到大排序
        sort(records.begin(),records.end());
        //这里由于要看记录是否配对,因此需要两个两个的比对
        for(int i = 0; i + 1 < records.size(); i ++){
            //代表记录合法
            if(records[i].status == "on-line" && records[i+1].status == "off-line"){
                //输出信息
                //  1.  输出第一条信息:名字+月份
                if(!total){
                    //当string使用%s输出时,需要加上方法c_str()
                    printf("%s %02d\n",name.c_str(),month);
                }
                //算出记录之间所经过的分钟数
                int minutes = records[i+1].minutes - records[i].minutes;
                //算出记录之间的花费
                double money = sum[records[i+1].minutes] - sum[records[i].minutes];
                //添加到总花费当中
                total += money;
                //  2.  输出第二条信息:合法记录的信息
                printf("%s %s %d $%.2lf\n",records[i].format_time.c_str(),records[i+1].format_time.c_str(),minutes,money);
            }
        }
        //如果存在总花费:输出第三条信息
        if(total){
            printf("Total amount: $%.2lf\n",total);
        }
    }
    
    return 0;
}
posted @   夏目^_^  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示