筛选有效数据+结构排序+统一化时间计算+map映射 名称:记录列表 1016 Phone Bills (25分)

1016 Phone Bills (25分)

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

解题
给出的数据是无序的,先把数据存入容器中,进行 姓名主排序,通话开始时间次排序;

再建立map映射 人名:有效通话记录,从已排序容器中得到有效的通话记录,即前一个通话记录状态为on,后一个为off;

1.结构对象,保存通话记录

struct records{
	string name;
	int status,month,time,day,hour,minute;
};

status用1表示接起,0表示挂断;
time表示从当月1号开始计算时间,即2460day+60*hour+minute,为当月时间;

2.比较函数,利用time可以更快速的进行比较
区分名字+月数+当月的时间
测试样例中只有同月的,没有多月的,所以也无需判断月份,但加上更好;

bool cmp(records a, records b)
{
	if(a.name==b.name){
		if(a.month==b.month){
			return a.time<b.time;
		}else return a.month<b.month;
	}else return a.name<b.name;
}

可写为

bool cmp(records a, records b)
{
	return a.name!=b.name? a.name<b.name:a.month!=b.month? a.month<b.month:a.time<b.time ;
}

比较帅;

  1. 计算time所用的总钱数,后面用总钱数相减得到每次钱数也方便;
double billFromZero(records call, int *rate){
	double total = rate[call.hour]*call.minute+rate[24]*60*call.day;
	for(int i=0;i<call.hour;i++){
		total+= rate[i]*60;
	}
	return total/100.0;
} 

从该月1日0开始计算总钱数,rate[24]存放打一天电话所需钱数;

4.main函数


int main(){
	int rate[25]={0};     //为计算24,需要置为9=0先 
	int N;
	for(int i=0;i<24;i++){
		scanf("%d",&rate[i]);
		rate[24]+=rate[i];        //rate24为一整天的rate 
	}
	
	scanf("%d",&N);
	vector<records> data(N);    //存放N条通话记录 
	
	for(int i=0;i<N;i++){
		cin>>data[i].name;
		scanf("%d:%d:%d:%d",&data[i].month,&data[i].day,&data[i].hour,&data[i].minute);
		string temp;
		cin>>temp;
		data[i].status = (temp=="on-line")?1:0;
		
		data[i].time=data[i].day*24*60+data[i].hour*60+data[i].minute;
	} 
	sort(data.begin(),data.end(),cmp);
	map<string,vector<records>> customer;
	//字典 名称:记录 
	//匹配符合条件的一对记录 
	for(int i=1;i<N;i++)
	{	//前者为on,后者为off,name相等 
		if(data[i].name==data[i-1].name&&data[i-1].status==1&&data[i].status==0){
			customer[data[i-1].name].push_back(data[i-1]);
			customer[data[i].name].push_back(data[i]);
			//两条有效记录插入 
		}
	}
	for(auto it:customer){
		vector<records> temp = it.second;
		cout<<it.first;
		printf(" %02d\n",temp[0].month);
		double total = 0.0f;
		for(int i=1;i<temp.size();i+=2)
		{
			//通过相对花费差额来计算真实花费
			double t = billFromZero(temp[i],rate) - billFromZero(temp[i-1],rate);
			printf("%02d:%02d:%02d %02d:%02d:%02d %d $%.2f\n",temp[i-1].day,temp[i-1].hour,temp[i-1].minute,temp[i].day,temp[i].hour,temp[i].minute,temp[i].time - temp[i-1].time,t);
			total+=t; 
		}
		printf("Total amount: $%.2f\n",total);
	}
	return 0;
}

利用time将每个记录的时间统一化,方便时间比较和钱数统计;

该代码缺点是不能将同一个人不同月份的通话记录区分开来,需要在输出上增加month的判断条件;
将main函数做如下修改,输出循环时增加月份判断代码,即可有效区别一个人不同月份的账单,并统计输出和该月份的账单总和;

		if(i>2&&temp[i].month!=temp[i-2].month)
			{
				printf("Total amount: $%.2f\n",total);
				cout<<it.first;
				printf(" %02d\n",temp[i].month);
				total = 0.0f;
			}

完整代码

#include
#include
#include
#include
using namespace std;

struct records{
	string name;
	int status,month,time,day,hour,minute;
};

bool cmp(records a, records b)
{
	if(a.name==b.name){
		if(a.month==b.month){
			return a.time<b.time;
		}else return a.month<b.month;
	}else return a.name<b.name;
}

//排序,名称短的在前/时间短的在前
//时间从1月1日0点开始算起

//计算每一个通话记录的相对开销 
//传入一个通话记录,得到这个记录的开销
//rate为每小时的收费标准; 
double billFromZero(records call, int *rate){
	double total = rate[call.hour]*call.minute+rate[24]*60*call.day;
	for(int i=0;i<call.hour;i++){
		total+= rate[i]*60;
	}
	return total/100.0;
} 


int main(){
	int rate[25]={0};     //为计算24,需要置为9=0先 
	int N;
	for(int i=0;i<24;i++){
		scanf("%d",&rate[i]);
		rate[24]+=rate[i];        //rate24为一整天的rate 
	}
	
	scanf("%d",&N);
	vector<records> data(N);    //存放N条通话记录 
	
	for(int i=0;i<N;i++){
		cin>>data[i].name;
		scanf("%d:%d:%d:%d",&data[i].month,&data[i].day,&data[i].hour,&data[i].minute);
		string temp;
		cin>>temp;
		data[i].status = (temp=="on-line")?1:0;
		
		data[i].time=data[i].day*24*60+data[i].hour*60+data[i].minute;
	} 
	sort(data.begin(),data.end(),cmp);
	map<string,vector<records>> customer;
	//字典 名称:记录 
	//匹配符合条件的一对记录 
	for(int i=1;i<N;i++)
	{	//前者为on,后者为off,name相等 
		if(data[i].name==data[i-1].name&&data[i-1].status==1&&data[i].status==0){
			customer[data[i-1].name].push_back(data[i-1]);
			customer[data[i].name].push_back(data[i]);
			//两条有效记录插入 
		}
	}
	for(auto it:customer){
		vector<records> temp = it.second;
		cout<<it.first;
		printf(" %02d\n",temp[0].month);
		double total = 0.0f;
		for(int i=1;i<temp.size();i+=2)
		{
			if(i>2&&temp[i].month!=temp[i-2].month)
			{
				printf("Total amount: $%.2f\n",total);
				cout<<it.first;
				printf(" %02d\n",temp[i].month);
				total = 0.0f;
			}
			//通过相对花费差额来计算真实花费
			double t = billFromZero(temp[i],rate) - billFromZero(temp[i-1],rate);
			printf("%02d:%02d:%02d %02d:%02d:%02d %d $%.2f\n",temp[i-1].day,temp[i-1].hour,temp[i-1].minute,temp[i].day,temp[i].hour,temp[i].minute,temp[i].time - temp[i-1].time,t);
			total+=t; 
		}
		printf("Total amount: $%.2f\n",total);
	}
	return 0;
}

总结
1.用map > 存放 人名:相匹配的通话记录 较为方便;
2.涉及时间计算,从0点0分,或该月1日0点0分统计更方便;
3.传入map时筛选有效数据,可以避免后续边输出边筛选时的错误;

你可能感兴趣的:(PAT)