题目大意:
给出24h中每个小时区间内的资费(cents/minute),并给出N条通话记录,每条通话记录都记录了姓名、当前时刻(月:日:时:分)以及其属于通话开始(on-line) 或是通话结束(off-line)。现在需要对每个人的有效通话记录进行资费计算,有效通话记录是指同一个用户能够配对的所有on-line和off-line,而这样的配对需要满足:在按时间顺序排列后,两条配对的on-line和off-line对应时间内不允许出现其他的on-line或off-line的记录。输出要求:按姓名的字典序从小到大的顺序输出存在有效通话记录的用户。对单个用来说,需要输出他的姓名、账单月份(题目保证单个用户的所有记录都在同一个月产生)以及有效通话记录的时长和花费,最后输出他的总资费。
算法思路:
由于需要对每一个用户的账单进行输出,所以这里采用按照用户分组的方式来处理,由于输出也是需要按照字典序进行输出,所以这里先对所有的记录进行排序,对于名字不同的按照字典序排序,对于名字相同的记录按照每条记录的产生时间从前往后排序。然后处理每一组用户,使用2个指针work_pointer和next_person,分别表示当前用户的工作指针和下一个用户的第一条记录的位置。在work_pointer和next_person之间的就是同一个用户。那么在同一用户之间只需要找到一对,也就是紧紧挨着的2条记录,上一条为on-line,下一条为off-line,这样就找到了一条有效记录,有可能会出现没有有效记录的情况,这里使用一个标识ispaired
来记录是否有有效记录,在生成账单的时候设置为true,在当前用户遍历完毕的时候,如果ispaired为true,那么就保留当前人的账单。并且更新work_pointer为next_person,进入下一个用户的遍历。
时间的处理:
对于月份使用一个变量month就可以保存,然后对于天,小时和分全部转化为分钟来进行处理。
一条有效记录费用的计算:
这里给出2种方法,第一种就是采用加法,每加一分钟就累计当前小时的每一分钟的费用,然后直到开始时间和结束时间相等。代码就是如下的
callOneRecordCharge函数:
// 计算一条记录的收费
double callOneRecordCharge(int start_time,int end_time){
int sdd = start_time/24/60;
int shh = start_time%(24*60)/60;
int smm = start_time%(24*60)%60;
double sum = 0;
while (start_time<end_time){
++smm; // 增加一分钟
sum += rate[shh]; // 累计每一分钟的收费
if (smm==60){// 每60分钟进1小时
shh++;
smm = 0;
if (shh==24){// 每24小时进1天
sdd++;
shh = 0;
}
}
++start_time;
}
return sum/100; // 美分转化为美元
}
第二种方法采用减法,先计算从00:00:00到开始时间的花费,然后计算从00:00:00到结束时间的花费,然后让后者减去前者得到开始时间到结束时间的花费,代码如下:
double cal(int time){//计算00:00:00到time的花费
int dd = time/(24*60);//天
int hh = time%(24*60)/60;//小时
int mm = time%60;//分钟
double charge = 0;//00:00:00到time时间的花费
for(int i=0;i<dd;++i){//计算在dd天之前的整天数的花费
charge += sum*60;
}
for(int i=0;i<hh;++i){//再计算dd天当天的hh小时之前的整小时花费
charge += 60*per_charge[i];
}
charge += per_charge[hh]*mm;//再计算在dd天的hh小时内打的mm分钟的花费
return charge;
}
double getCharge(int start,int end){//根据起始时间和截止时间计算花费(美元,1美元=100美分)
double charge = cal(end)-cal(start);//start到end时间的花费
return charge/100;
}
这里采用了第一种方法
注意点:
1、资费的输出需要进行单位换算,即把cent换算为dollar,所以结果要除以100。
2、同一用户的通话记录按照时间先后进排序输出。这里使用了map就是会自动排序
3、测试点1,2,3都是在判断是否会输出无效记录,这里给出一组数据供参考,如果输出了3个用户就是错的。
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 on-line
CYJJ 01:01:07:00 off-line
CYLL 01:01:08:03 on-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 on-line
4、输出格式中一般整型都得是%02d的格式,但是单条记录的花费直接用%d,总花费得用%.2f
5、work_pointer<N-1这个不需要写成work_pointer<N,因为work_pointer=N-1的时候肯定没有off-line与之匹配了.
提交结果:
AC代码:
#include<cstdio>
#include<vector>
#include<algorithm>
#include<string>
#include<map>
#include <cstring>
using namespace std;
int rate[24];// 每小时区间的每分钟收费率
int month;// 月份
struct Record{
string name;// 顾客的名字
int time; // 该记录产生的时间
bool isOnline; // 是否是on-line记录
};
struct Bill{
string name;// 顾客的名字
map<int,int> pair_time;// 所有通话记录,key为起始时间,value为结束时间
};
// 将时间转化为分钟
int getMinutes(int dd,int hh,int mm){
return dd*24*60+hh*60+mm;
}
// 计算一条记录的收费
double callOneRecordCharge(int start_time,int end_time){
int sdd = start_time/24/60;
int shh = start_time%(24*60)/60;
int smm = start_time%(24*60)%60;
double sum = 0;
while (start_time<end_time){
++smm; // 增加一分钟
sum += rate[shh]; // 累计每一分钟的收费
if (smm==60){// 每60分钟进1小时
shh++;
smm = 0;
if (shh==24){// 每24小时进1天
sdd++;
shh = 0;
}
}
++start_time;
}
return sum/100; // 美分转化为美元
}
// 排序函数,先按照人名分组,然后对每个人的记录按照时间进行排序
bool cmp(Record a,Record b){
if (a.name!=b.name){
return a.name<b.name;
} else{
return a.time<b.time;
}
}
// 输出一个人的账单记录
void printBillRecord(Bill bill){
printf("%s %02d\n",bill.name.c_str(),month);
map<int,int>::iterator iter;
double total_amount = 0;
for (iter=bill.pair_time.begin();iter!=bill.pair_time.end();++iter){
int start_time = iter->first; // 拨通电话时间
int end_time = iter->second; // 挂断电话时间
int sdd = start_time/24/60;
int shh = start_time%(24*60)/60;
int smm = start_time%(24*60)%60;
int edd = end_time/24/60;
int ehh = end_time%(24*60)/60;
int emm = end_time%(24*60)%60;
double amount = callOneRecordCharge(start_time,end_time);
total_amount += amount;
printf("%02d:%02d:%02d %02d:%02d:%02d %d $%.2lf\n",sdd,shh,smm,edd,ehh,emm,end_time-start_time,amount);
}
printf("Total amount: $%.2lf\n",total_amount);
}
int main(){
for (int i = 0; i < 24; ++i) {
scanf("%d",&rate[i]);
}
int N;//记录条数
scanf("%d",&N);
char name[30];
int dd,hh,mm;
char line[10];
vector<Record> records;
for (int j = 0; j < N; ++j) {
Record record;
scanf("%s %d:%d:%d:%d %s",name,&month,&dd,&hh,&mm,line);
record.name = name;
record.isOnline = strcmp(line,"on-line")==0;
record.time = getMinutes(dd,hh,mm);
records.push_back(record);
}
// 排序
sort(records.begin(),records.end(),cmp);
// 生成所有人的账单
int work_pointer = 0;// 工作指针,初始指向每个人记录的第一个,之后指向第一个为on-line的记录
int next_person = 0; // 记录下一个人的初始记录的位置
vector<Bill> bills;// 所有人的账单
while (work_pointer<N-1){
// 首先找到下一个人的位置
while (records[next_person].name==records[work_pointer].name){
++next_person;
}
Bill bill;// 单个人的账单
bool ispaired = false; // 是否有匹配的通话记录
// 减一是因为一定得是一组on-line和off-line的记录才有意义
while (work_pointer<next_person-1){
// 首先找到第一个为on-line,第二个人为off-line
while (work_pointer<next_person-1&&!(records[work_pointer].isOnline&&!records[work_pointer+1].isOnline)){
++work_pointer;
}
// 没有找到匹配的记录
if (work_pointer==next_person-1){
break;
}
// work_pointer的下一条记录为off-line,生成账单
bill.name = records[work_pointer].name;
bill.pair_time[records[work_pointer].time] = records[work_pointer+1].time;
work_pointer += 2;
ispaired = true;
}
if (ispaired){
// 当前用户的账单生成成功
bills.push_back(bill);
}
// 更新工作指针,指向下一个人
work_pointer = next_person;
}
// 输出账单记录
for (int k = 0; k < bills.size(); ++k) {
printBillRecord(bills[k]);
}
return 0;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。