甲级PAT 1016 Phone Bills(这是到目前为止甲级写的最长最复杂的代码,其实并不难,逻辑理顺了就好)

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

一开始要写这题解析我是很崩溃的。。。尤其是刚写完这么长的代码。压根不想重新看呐(拍桌) ,光写完这题就花了三个小时。。写博客又花了两小时。。这要是pat考试怕是已经凉了

题目要求:

 这是一道和生活息息相关的题目。一家长途电话收费公司需要根据所有的通话记录打印每位客户的账单。每项通话记录中包含用户的姓名、通话记录时间,通话记录状态(可以理解为on-line是在打电话,off-line是在挂电话)。通话记录时间对应mm:dd:hh:mm分别表示月份、日期、小时、分钟。需要找到同一个用户按时间顺序匹配的通话记录(即同一用户距离最近的on-line和off-line,这里可以看示例给的aaa用户,第一个on-line时间应该是01:01:03,而最后账单打印的是02:00:0,所以是在off-line前的最后一个on-line,其他没有配对的忽视掉)计算这些能匹配通话记录对应的持续时间、花费以及某个用户所有通话记录的总花费。

公司的收费是根据每个时间段一天24个时间段。每个时间对应不同的收费标准,第一行给出时间段00:00-01:00 、01:00-02:00、02:00-03:00……23:00-00:00这24个时间段对应每分钟多少美分收费标准。所以每次通话记录的收费都要按时间段来计算

解题思路:

首先想好怎么存储这些数据以及输出的数据。题目中一共有两张表,一个是所有通话记录的表,还有一个是打印输出的表,这两个中的内容不一样,所以利用两个结构体Call和Bill(用一维数组存储多个人信息)分别存储,Call中包含用户的姓名、通话记录时间,通话记录状态,Bill包含用户姓名、通话月份、通话详情信息(这个再用一个结构体DetailBill存储)、总花费、通话详情信息的个数(因为每个通话详情信息个数可能不一样,需要标注)。还要明确需要写几个函数:两种排序(一个通话记录按时间排序,一个总账单按姓名排序)、查找匹配off-line、计算持续时间、计算花费

然后就是开始慢慢解题。。。这里首先将所有的通话记录按时间从小到大进行排序,这个可以利用重写sort排序的比较函数。排完序后从前往后找当前通话记录状态为on-line的用户对应的off-line,因为这里要找离off-line最近的on-line,若在查找过程中发现有该用户的通话记录的on-line则当前通话记录匹配失败,继续下一个通话记录。

对于匹配成功的接着进行下面的运算,这里on-line下标i,off-line下标matchi。首先查找该用户姓名是否在总表bill中,如果不在新存入表中,若在则增加当前表中的通话详情。然后就是把各种信息计算保存到总表账单中。

注意:

1.在计算持续时间以及花费的时候先将每个位数转化为对应的Int型方便计算。

2.计算持续时间要注意分钟、小时、天数的转换。例如22:18:17和23:06:16。分钟的计算不是直接把分钟大的减去分钟小的,而是把总时间大的减去总时间小的,这里肯定是第二个总时间大,23号>22号。所以分钟应是16向前06借60,为76。利用76-17 = 59分钟。此时第二个时间小时为05,05<18所以要向天数借1,也就是05+24 =29,利用29-18=11小时。可以自己推算一下这两个时间差正好是11小时59分

3.计算花费要注意和时间段进行匹配。 因为有的时间差可能相差几天这样,先将天数的差乘以24全部转化为小时的时间差。

接下来计算时间差有两种情况,一个是两个小时相等,假设时间分别是02:06:06和02:06:25,只要减分钟就可以了。

另一种小时不相等,比如03:06:06和04:06:25,首先将04:06:25转化为03:30:25。第一个时间段计费06:06-07:00,第二个时间段07:00-08:00……最后一个时间段06:00-06:25,所以只要处理第一次把60-06=54,除了最后一次剩下的全部是60-0。 每次计算完时间段的花费,时间段向后挪一个就好了,将大于等于24小时转化为时间段只需要对24取余就可以了,这样24对应0,25对应1…… 。这里要注意循环条件不能写成for(i=0;i<=calldatetime[n][2]-calldatetime[m][2];i++) 而要写成j = calldatetime[n][2]-calldatetime[m][2]; for(i=0;i<=j;i++){},不然的话就会少执行循环次数,出现错误。

4.姓名从小大大排序,按字母顺序 这里就按ASCII码排序就可以了,'a' 97>'A' 65,所以大写字母都排在前面,当然对于aa和aaa当然是aa排在前面,aaa排在后面。

5.在计算花费是根据分钟和对应每分钟多少美分计算,最后要求的单位是美元,所以要将美分转化为美元。。1美元=100美分

6.做这种题一定要耐心+细心,少稍不注意就有错的地方

完整代码:

#include<iostream>
#include<string>
#include<sstream>
#include<cstring> 
#include<iomanip>
#include<algorithm>
using namespace std;
#define maxsize 1001

//所有的通话信息 
typedef struct call{
	string name; //姓名 
	string datetime; //电话日期 
	string state; //on-line or off-line 
}Call;

//每个用户详细的单次通话信息 
typedef struct DetailRecord{
	string starttime; //开始时间 
	string endtime; //结束时间 
	int lastingtime; //持续时间 
	float cost; //单次通话花费 
}DetailRecord;

//每个用户的账单 
typedef struct Bill{
	string name; //姓名 
	string month; //月份 
	DetailRecord record[maxsize/2]; //详细账单列表 
	float totalcost; //总花费 
	int recordnum; //记录每个人的详细账单个数 
}Bill;

int price[25];  //每个时间段花费 
Call call[maxsize]; //存储所有的通话信息 
Bill bill[maxsize/2]; //存储所有用户的账单信息
int billnum = 0; //记录bill个数,从0开始存储  
int N;
int calldatetime[maxsize][4]; //每个通话时间存储为int, 分别对应月份,日期,小时,分钟

//将所有通话信息按时间从小到大排序 
bool comp(Call call1,Call call2){
	int intarray[8]; //每位分别对应 call1 的月份,日期,小时,分钟call2的月份,日期,小时,分钟;
	stringstream ss;
	int i,j = 0;
	for(i=0;i<8;i++){
		if(i==4) j=0;
		if(i<4){
			ss << call1.datetime.substr(j,j+2);
			ss >> intarray[i];
		}else{
			ss << call2.datetime.substr(j,j+2);
			ss >> intarray[i];		
		}
		ss.clear();
		ss.str("");
		j+=3;	
	}
	if(intarray[0]<intarray[4]) return true; //比较月份 
	else if(intarray[0] == intarray[4]){
		if(intarray[1]<intarray[5]) return true; //比较日期 
		else if(intarray[1] == intarray[5]){
			if(intarray[2]<intarray[6]) return true; //比较小时
			else if(intarray[2] == intarray[6]){
				if(intarray[3]<intarray[7]) return true; //比较分钟 
				else return false;		
			}
			else return false;	
		}
		else return false;
	}
	else return false;
}

/*找到第x通话记录对应的同一用户与on-line记录最近匹配的off-line,
若匹配到返回对应在所有通话信息的下标,
反之返回-1,
若第x通话记录后再次出现同一用户on-line,也返回-1 */
int findmatch(int x){
	int i;
	for(i=x+1;i<N;i++){
		if(call[i].name==call[x].name){
			if(call[i].state=="off-line") return i;
			else return -1;
		}
	}
	if(i == N) return -1;
}

//将第i个通话记录对应的月份,日期,小时,分钟转化为整型calldatetime[i][0],calldatetime[i][1],calldatetime[i][2],calldatetime[i][3]
void trans(int i){
	int k,j=0;
	stringstream ss;
	for(k=0;k<4;k++){
		ss << call[i].datetime.substr(j,j+2);
		ss >> calldatetime[i][k];
		ss.clear();
		ss.str("");
		j+=3;
	}
} 

//计算第m个通话记录与第n个通话记录的持续时间 (这个计算大家先自己算一下就能理解了) 
int countlastingtime(int m,int n){
	int lastingtime=0;
	trans(m);
	trans(n);
	if(calldatetime[n][3] > calldatetime[m][3]){ //比较分钟 
		lastingtime = lastingtime + calldatetime[n][3] - calldatetime[m][3]; 
	}else if(calldatetime[n][3] < calldatetime[m][3]){ //若off-line的分钟要小于on-line的分钟则向小时借60分钟
		calldatetime[n][2]--;
		calldatetime[n][3]+=60;
		lastingtime = lastingtime + calldatetime[n][3] - calldatetime[m][3];
	}
	if(calldatetime[n][2] > calldatetime[m][2]){ //比较小时 
		lastingtime = lastingtime + (calldatetime[n][2] - calldatetime[m][2])*60; 
	}else if(calldatetime[n][2] < calldatetime[m][2]){
		calldatetime[n][1]--;
		calldatetime[n][2]+=24;
		lastingtime = lastingtime + (calldatetime[n][2] - calldatetime[m][2])*60;
	}
	if(calldatetime[n][1] > calldatetime[m][1]){ //比较日期 
		lastingtime = lastingtime + (calldatetime[n][1] - calldatetime[m][1])*24*60;
	}
	return lastingtime;
}

//计算第m个通话记录与第n个通话记录的花费 (注意算的时候是美分,返回为美元,1美元=100美分 
float countcost(int m,int n){
	int i,j,timesection;
	float cost = 0;
	trans(m);
	trans(n);
	calldatetime[n][2] += (calldatetime[n][1] - calldatetime[m][1])*24;
	j = calldatetime[n][2]-calldatetime[m][2];
	for(i=0;i<=j;i++){
		if(calldatetime[n][2] == calldatetime[m][2]){
			timesection = calldatetime[m][2]%24;
			cost += price[timesection] * (calldatetime[n][3] - calldatetime[m][3]);
		}else{
			timesection = calldatetime[m][2]%24;
			cost += price[timesection] * (60 - calldatetime[m][3]);
			calldatetime[m][3] = 0;
			calldatetime[m][2]++;
		}
	}
	cost = cost/100.0;
	return cost;
}

//将最后的总账单按字母顺序排序 
bool compa(Bill bill1,Bill bill2){
	string name1 = bill1.name;
	string name2 = bill2.name;
	int i,j;
	for(i=0;i<name1.length();i++){
		if(i==name2.length()) return false;
 		if(int(name1[i])<int(name2[i])) return true;
		else if(int(name1[i])>int(name2[i])) return false;
	}
	if(i==name1.length()) return true;
}

int main(){
	ios::sync_with_stdio(false);
	int i,j,matchi,recordnum;
	float sum;
	string name; 
	for(i=0;i<24;i++){
		cin>>price[i];
	}
	cin>>N;
	for(i=0;i<N;i++){
		cin>>call[i].name>>call[i].datetime>>call[i].state;
	}
	sort(call,call+N,comp);
	for(i=0;i<N;i++){
		if(call[i].state=="on-line"){
			matchi = findmatch(i);
			if(matchi > 0){
				name = call[i].name;
				//查找该用户是否在账单总表里 
				for(j=0;j<billnum;j++){
					if(bill[j].name == name) break;
				}
				if(j==billnum){ //不在账单总表加入 
					bill[j].name = name;
					bill[j].month = call[i].datetime.substr(0,2);
					bill[j].record[0].starttime = call[i].datetime.substr(3,11);
					bill[j].record[0].endtime = call[matchi].datetime.substr(3,11);
					bill[j].record[0].lastingtime = countlastingtime(i,matchi);
					bill[j].record[0].cost = countcost(i,matchi);
					bill[j].recordnum = 1;
					billnum++;	
				}else{		
					recordnum = bill[j].recordnum;
					bill[j].record[recordnum].starttime = call[i].datetime.substr(3,11);
					bill[j].record[recordnum].endtime = call[matchi].datetime.substr(3,11);
					bill[j].record[recordnum].lastingtime = countlastingtime(i,matchi);
					bill[j].record[recordnum].cost = countcost(i,matchi);
					bill[j].recordnum++;
				}
			}
		}
	}
	sort(bill,bill+billnum,compa);
	for(i=0;i<billnum;i++){
		sum = 0;
		cout<<bill[i].name<<" "<<bill[i].month<<endl;
		for(j=0;j<bill[i].recordnum;j++){
			cout<<bill[i].record[j].starttime<<" "<<bill[i].record[j].endtime<<" "<<bill[i].record[j].lastingtime<<" $"<<fixed<<setprecision(2)<<bill[i].record[j].cost<<endl;
			sum += bill[i].record[j].cost;
		}
		cout<<"Total amount: $"<<fixed<<setprecision(2)<<sum<<endl;
	} 
	return 0;
} 

小技巧:

1.将字符串转化为int数组用stringstream,在同一个函数用多次需要清空一下。ss.clear();ss.str("")

2.对字符串截取用substr(0,2)是截取第0位和第1位,第二位不算

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值