每日两道算法题-|Day14 of PAT----|1016 Phone Bills (25point(s))--|结构体排序以及Map映射使用--|详解

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


看到这类题目,我们大致要做到心中有数,看到输入和输出的格式,基本上就是结构体排序以及相关问题了,接下来我们只要带着问题去看题目,最终确定排序的标尺和其他功能的要求就可以了,再配合上
vector和sort函数使用即可。
题目要求大致如下:
输入的第一行会给你24小时的通话费率,
第二行会告诉你接下来通信记录的条数
然后便是所有的通话记录
要求按照人名分类输出通话记录和费用(单次通话费用以及该人总的通话费用)


Attention:

  • 明眼人都能看出此题的输入量不可小觑,所以为了不在有的测试用例中因为输入输出导致超时,小心起见我们还是使用scanf和printf来进行输入输出比较稳妥。

Details:

先定义结构体来定义零散通话记录的格式以及内容,通过输入,我们不难确定每条通话记录其中包括通话人姓名,一个月、日、时、分的时间记录,一个通话开始或者结束的标记,所以我们不难确定结构体内容:

typedef struct record{
    string name;
    Time t;
    bool online;
    record(string na, int m, int d, int h, int mm, bool tag): name(na), t(m,d,h,mm), online(tag){}
}record;

当然,为了在账单结构中复用,在这里额外定义了一个Time结构体来辅助存放时间

typedef struct Time{
    int day;
    int hour;
    int min;
    int month; 
    Time(const Time &t): month(t.month), day(t.day), hour(t.hour), min(t.min){}
    Time(int m, int d, int h, int mm): month(m), day(d),hour(h),min(mm){}
}Time;

应题目要求,我们获得了零散的通话记录,现在要对其进行排序操作,按照先人名,后时间来进行排序

bool cmpRec(const record &a, const record &b){
        if(a.name.compare(b.name)!=0)   return (a.name< b.name);
        else if(a.t.month!=b.t.month) return (a.t.month<b.t.month);
        else if(a.t.day!=b.t.day) return (a.t.day<b.t.day);
        else if(a.t.hour!=b.t.hour)   return (a.t.hour<b.t.hour);
        else    return (a.t.min<b.t.min);
}
sort(recordList.begin(),recordList.end(),cmpRec);

然后将这些零散记录进行挑选和规整,对于已经按照名字和时间顺序排列好的零散记录,我们只需要进行遍历,将相邻且前者为始后者为末的记录进行配对并和并成为新的账单记录并按照姓名的区别放入容器中即可。

这里就需要用到一个map类,来完成姓名与记录的映射

map<string, vector<bill> > billMap;//此处为一对多的映射

遍历选择以及插入代码如下

 for(int i=0;i<recordList.size()-1;i++){
        if(recordList[i].name.compare(recordList[i+1].name)==0&&recordList[i].online==true&&recordList[i+1].online==false){
            bill temp(recordList[i].name,recordList[i].t,recordList[i+1].t);
            billMap[recordList[i].name].push_back(temp);
        }
    }

当然咯,在进行配对的时候,不免地要设计单次通话费率计算的问题。

一开始我准备用运算技巧来实现而不是一分一分的加上去,后来发现漏洞百出,又想到计算机每秒10的9次方级的运算速率,这些加法运算实在是没必要做优化,更何况如果是在考场上,讲究一个快和准,直接将日常生活中的逻辑用代码和算法实现就行了,实在没那么优雅也没的办法:

 bill(string na, Time t1, Time t2): name(na), t_1(t1), t_2(t2){
        this->len=(t2.day-t1.day)*24*60+(t2.hour-t1.hour)*60+(t2.min-t1.min);
        int min=0;
        double amount=0.0;
		while (t1.day!=t2.day || t1.hour!=t2.hour || t1.min!=t2.min){
			amount+=toll[t1.hour];
			t1.min++;
			min++;
			if (t1.min==60){
				t1.hour++;
				t1.min=0;
			}
			if (t1.hour==24){
				t1.day++;
				t1.hour=0;
			}
		}
        this->cost=amount/100.00;      
    }

到这一步,我们已经完成了90%的工作,最后只要按照姓名取出,并算出总费用,最后输出即可

 for(std::map< string , vector< bill > >::iterator it = billMap.begin();it!=billMap.end();it++){ 
        cout<<it->first;
        vector<bill> billList = it->second;   
		printf(" %02d\n", billList[0].t_1.month);  
        double total=0;
        for(std::vector<bill>::iterator i = billList.begin();i!=billList.end();i++){
            printf("%02d:%02d:%02d %02d:%02d:%02d %d $%.2f\n", i->t_1.day, i->t_1.hour, i->t_1.min,
                                                               i->t_2.day, i->t_2.hour, i->t_2.min, i->len, i->cost); 
            total+=i->cost;
        }
        printf("Total amount: $%.2f\n", total);
    }

完整代码如下:


Code:

#include <bits/stdc++.h>
using namespace std;
int toll[24];
int recordNum;
typedef struct Time{
    int day;
    int hour;
    int min;
    int month; 
    Time(const Time &t): month(t.month), day(t.day), hour(t.hour), min(t.min){}
    Time(int m, int d, int h, int mm): month(m), day(d),hour(h),min(mm){}
}Time;
typedef struct record{
    string name;
    Time t;
    bool online;
    record(string na, int m, int d, int h, int mm, bool tag): name(na), t(m,d,h,mm), online(tag){}
}record;
bool cmpRec(const record &a, const record &b){
        if(a.name.compare(b.name)!=0)   return (a.name< b.name);
        else if(a.t.month!=b.t.month) return (a.t.month<b.t.month);
        else if(a.t.day!=b.t.day) return (a.t.day<b.t.day);
        else if(a.t.hour!=b.t.hour)   return (a.t.hour<b.t.hour);
        else    return (a.t.min<b.t.min);
}
typedef struct bill{
    string name;
    Time t_1;
    Time t_2;
    int len;
    double cost;
    bill(string na, Time t1, Time t2): name(na), t_1(t1), t_2(t2){
        this->len=(t2.day-t1.day)*24*60+(t2.hour-t1.hour)*60+(t2.min-t1.min);
        int min=0;
        double amount=0.0;
		while (t1.day!=t2.day || t1.hour!=t2.hour || t1.min!=t2.min){
			amount+=toll[t1.hour];
			t1.min++;
			min++;
			if (t1.min==60){
				t1.hour++;
				t1.min=0;
			}
			if (t1.hour==24){
				t1.day++;
				t1.hour=0;
			}
		}
        this->cost=amount/100.00;      
    }
}bill;
vector<record> recordList;
map<string, vector<bill> > billMap;
int main(){
    ///read toll and recordNum
    for(int i=0;i<24;i++)
        scanf("%d", &toll[i]);
    scanf("%d", &recordNum);
    ///read record
    for(int i=0;i<recordNum;i++){
        string name, tag;
        int month, day, hour, min;
        bool online;
        cin>>name;
        scanf("%d:%d:%d:%d", &month, &day, &hour, &min);
        cin>>tag;
        if(tag=="on-line")  online=true;
        else    online=false;
        record temp(name, month, day, hour, min, online);
        recordList.push_back(temp);
    }
    ///make records sorted and paired, and insert into bill
    sort(recordList.begin(),recordList.end(),cmpRec);
    for(int i=0;i<recordList.size()-1;i++){
        if(recordList[i].name.compare(recordList[i+1].name)==0&&recordList[i].online==true&&recordList[i+1].online==false){
            bill temp(recordList[i].name,recordList[i].t,recordList[i+1].t);
            billMap[recordList[i].name].push_back(temp);
        }
    }
    for(std::map< string , vector< bill > >::iterator it = billMap.begin();it!=billMap.end();it++){ 
        cout<<it->first;
        vector<bill> billList = it->second;   
		printf(" %02d\n", billList[0].t_1.month);  
        double total=0;
        for(std::vector<bill>::iterator i = billList.begin();i!=billList.end();i++){
            printf("%02d:%02d:%02d %02d:%02d:%02d %d $%.2f\n", i->t_1.day, i->t_1.hour, i->t_1.min,
                                                               i->t_2.day, i->t_2.hour, i->t_2.min, i->len, i->cost); 
            total+=i->cost;
        }
        printf("Total amount: $%.2f\n", total);
    }
    return 0;
}

Summary and harvest

(for my current level)

Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力。由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个按照手机号-对方号码-时间戳方式时间倒序查询 HBase 表中话费清单的示例代码: ```java import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.Connection; import org.apache.hadoop.hbase.client.ConnectionFactory; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.client.Table; import org.apache.hadoop.hbase.filter.FilterList; import org.apache.hadoop.hbase.filter.PrefixFilter; import org.apache.hadoop.hbase.filter.SingleColumnValueFilter; import org.apache.hadoop.hbase.filter.CompareFilter; import org.apache.hadoop.hbase.filter.BinaryComparator; import org.apache.hadoop.hbase.filter.Filter; import org.apache.hadoop.hbase.util.Bytes; public class HBaseDemo { public static void main(String[] args) throws IOException { Configuration config = new Configuration(); config.set("hbase.zookeeper.quorum", "localhost"); // HBase ZooKeeper 地址 Connection connection = ConnectionFactory.createConnection(config); Table table = connection.getTable(TableName.valueOf("phone_bills")); // 话费清单表名 String phoneNum = "13800138000"; // 手机号码 byte[] startRow = Bytes.toBytes(phoneNum); // 起始行键 byte[] stopRow = Bytes.toBytes(phoneNum + "z"); // 终止行键 // 创建过滤器列表 FilterList filters = new FilterList(FilterList.Operator.MUST_PASS_ALL); // 添加前缀过滤器 Filter prefixFilter = new PrefixFilter(startRow); filters.addFilter(prefixFilter); // 添加列值过滤器 byte[] family = Bytes.toBytes("cf"); // 列族 byte[] qualifier1 = Bytes.toBytes("callee"); // 对方号码列名 byte[] qualifier2 = Bytes.toBytes("timestamp"); // 时间戳列名 byte[] value = Bytes.toBytes("139"); // 以 139 开头的号码 SingleColumnValueFilter valueFilter = new SingleColumnValueFilter( family, qualifier1, CompareFilter.CompareOp.EQUAL, value); filters.addFilter(valueFilter); // 创建扫描器 Scan scan = new Scan(); scan.setFilter(filters); scan.setStartRow(startRow); scan.setStopRow(stopRow); scan.addFamily(family); scan.addColumn(family, qualifier1); scan.addColumn(family, qualifier2); scan.setReversed(true); // 按时间倒序排列 // 执行扫描 ResultScanner scanner = table.getScanner(scan); // 遍历结果 List<String> result = new ArrayList<String>(); for (Result r : scanner) { String rowKey = Bytes.toString(r.getRow()); String callee = Bytes.toString(r.getValue(family, qualifier1)); String timestamp = Bytes.toString(r.getValue(family, qualifier2)); result.add(rowKey + "\t" + callee + "\t" + timestamp); } scanner.close(); // 反转结果列表 Collections.reverse(result); // 输出结果 for (String s : result) { System.out.println(s); } table.close(); connection.close(); } } ``` 需要注意的是,以上代码仅供参考,具体实现方式可能需要根据你的表结构和查询需求进行适当调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值