1016 Phone Bills

Posted CSU迦叶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1016 Phone Bills相关的知识,希望对你有一定的参考价值。

目录

概述:

一些小的注意点

AC代码


概述:

这道题是我迄今做出来的最复杂的一道PAT了,该题被归类到排序专题下,其实还涉及到大量的字符串处理等别的我暂时也说不出的知识点。

排序函数我写了两个,1是cmp,用于让记录按照时间从早到晚的顺序排列,2是cmp2,让用户按照姓名的字母顺序从小到大排列,可以说是很简单的两个函数。

结构体写了三个,分别是record,user,call。其中user包含call数组。record用于读入记录,属性对应输入。call是每一对记录(on-line和off-line),包括开始结束时间,分钟数,话费。user是每个用户,call_num代表记录的条数,用于最后的输出使用。

struct call{
	char startTimeExMonth[15];
	char endTimeExMonth[15];
	int call_minute = 0;
	double call_money = 0;
};

struct user{
	char name[22] = "";
	char month[3] = ""; 
	call calls[maxn];
	int call_num = 0;
	double total_money = 0;
}users[maxn];

struct record{
	char name[22] = "";
	char time[15] = "";
	char state[10] = "";
}recs[maxn][maxn];

除了比较函数外还写了两个函数,getMinute输入开始和结束时间,输出总共的分钟数。getMoney输入开始、结束时间和单位话费数组,输出美元数(1美元=100美分)。

这两个函数一开始都有让我共同纠结的点,比如遇到大单位数字小,小单位数字大两个数如何相减。其实可以放心使用 x - y = (x-0)-(y-0)

int getMinute(char startTime[15],char endTime[15]){
	int startMonth = 0;
	int startDay = 0;
	int startHour = 0;
	int startMin = 0;
	int endMonth = 0;
	int endDay = 0;
	int endHour = 0;
	int endMin = 0;
	sscanf(startTime,"%d:%d:%d:%d",&startMonth,&startDay,&startHour,&startMin);
	sscanf(endTime,"%d:%d:%d:%d",&endMonth,&endDay,&endHour,&endMin);
	//01:02:00:01 01:04:23:59 4318
	return (endDay*24*60+endHour*60+endMin) - (startDay*24*60+startHour*60+startMin);
}



double getMoney(char startTime[15],char endTime[15],int tolls[24]){
	int startMonth = 0;
	int startDay = 0;
	int startHour = 0;
	int startMin = 0;
	int endMonth = 0;
	int endDay = 0;
	int endHour = 0;
	int endMin = 0;
	sscanf(startTime,"%d:%d:%d:%d",&startMonth,&startDay,&startHour,&startMin);
	sscanf(endTime,"%d:%d:%d:%d",&endMonth,&endDay,&endHour,&endMin);
	
	int fullday_money = 0;//一整天的话费 
	for(int i=0;i<24;i++){
		fullday_money += 60*tolls[i];
	}
	
	int start_minute_money = 0;//假设从0点开始打,打到整分的话费
	for(int i=0;i<startHour;i++){
		start_minute_money += 60*tolls[i];
	}
	int end_minute_money = 0;
	for(int i=0;i<endHour;i++){
		end_minute_money += 60*tolls[i];
	}
	 
	
	int startMoney = startDay*fullday_money+start_minute_money+tolls[startHour]*startMin;
	int endMoney = endDay*fullday_money+end_minute_money+tolls[endHour]*endMin;
	
	return (endMoney-startMoney)/100.0;
}

一些小的注意点

1. 怎样比较时间先后:由于时间是以

01:01:06:01

月:日:时:分的形式从大到小排列的字符串,所以在cmp中写strcmp即可。 

2. 怎样得到一对时间,对于每一个人按照时间排好顺序的记录,判断本条是否状态为在线且下条状态为离线,注意,由于相等时strcmp返回的是0,要记得加取反符号

if(!strcmp(recs[i][j].state,"on-line")&&!strcmp(recs[i][j+1].state,"off-line")) 

3. 那么多条记录,怎样将某个人的记录归类,这是我第一次做。开辟一个二维结构体数组,第一维表示每个人,通过strcmp对比当前读入的人名和每一维第一个元素的人名,如果有则为那一行的列数加一,如果没有就增加行数。 

for(int i=0;i<n;i++){
		bool hasFound = false;
		scanf("%s %s %s",name,time,state);
		//通过字符串比对姓名,看能否找到他
		for(int j=0;j<row;j++){
			if(strcmp(recs[j][0].name,name)==0){
				strcpy(recs[j][cols[j]].name,name);
				strcpy(recs[j][cols[j]].time,time);
				strcpy(recs[j][cols[j]].state,state);
				cols[j]++;
				hasFound = true;
				break;
			}
		}
		if(!hasFound){//没找到他,新开一列 
			strcpy(recs[row][0].name,name);
			strcpy(recs[row][0].time,time);
			strcpy(recs[row][0].state,state);
			cols[row]++;
			row++;
		} 
	}

4. 一开始我的2,3测试点是错误的,看了别的博客才知道,没有进行合法性校验,在读题时把每个测试用例至少有一对记录合法理解成每个人至少有一对记录合法。正确方式是如果某人没有记录,应当不予以输出,即加入如下代码:

const double eps = 1e-3;
if(abs(users[i].total_money-0)<eps)continue;

AC代码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<bits/stdc++.h>
using namespace std;

const int maxn = 1010;
const double eps = 1e-3;

struct call{
	char startTimeExMonth[15];
	char endTimeExMonth[15];
	int call_minute = 0;
	double call_money = 0;
};

struct user{
	char name[22] = "";
	char month[3] = ""; 
	call calls[maxn];
	int call_num = 0;
	double total_money = 0;
}users[maxn];

struct record{
	char name[22] = "";
	char time[15] = "";
	char state[10] = "";
}recs[maxn][maxn];

bool cmp(record a,record b){
	return strcmp(a.time,b.time)<0;
}

bool cmp2(user a,user b){
	return strcmp(a.name,b.name)<0;
}

int getMinute(char startTime[15],char endTime[15]){
	int startMonth = 0;
	int startDay = 0;
	int startHour = 0;
	int startMin = 0;
	int endMonth = 0;
	int endDay = 0;
	int endHour = 0;
	int endMin = 0;
	sscanf(startTime,"%d:%d:%d:%d",&startMonth,&startDay,&startHour,&startMin);
	sscanf(endTime,"%d:%d:%d:%d",&endMonth,&endDay,&endHour,&endMin);
	//01:02:00:01 01:04:23:59 4318
	return (endDay*24*60+endHour*60+endMin) - (startDay*24*60+startHour*60+startMin);
}



double getMoney(char startTime[15],char endTime[15],int tolls[24]){
	int startMonth = 0;
	int startDay = 0;
	int startHour = 0;
	int startMin = 0;
	int endMonth = 0;
	int endDay = 0;
	int endHour = 0;
	int endMin = 0;
	sscanf(startTime,"%d:%d:%d:%d",&startMonth,&startDay,&startHour,&startMin);
	sscanf(endTime,"%d:%d:%d:%d",&endMonth,&endDay,&endHour,&endMin);
	
	int fullday_money = 0;//一整天的话费 
	for(int i=0;i<24;i++){
		fullday_money += 60*tolls[i];
	}
	
	int start_minute_money = 0;//假设从0点开始打,打到整分的话费
	for(int i=0;i<startHour;i++){
		start_minute_money += 60*tolls[i];
	}
	int end_minute_money = 0;
	for(int i=0;i<endHour;i++){
		end_minute_money += 60*tolls[i];
	}
	 
	
	int startMoney = startDay*fullday_money+start_minute_money+tolls[startHour]*startMin;
	int endMoney = endDay*fullday_money+end_minute_money+tolls[endHour]*endMin;
	
	return (endMoney-startMoney)/100.0;
}



int main(){

	
	int row = 0;//行,二维结构体数组的第一维下标,代表某个用户
	int cols[maxn] = {0};//记录每一行的列数 
	//读入每个时段的电话费
	int tolls[24];
	for(int i=0;i<24;i++){
		scanf("%d",&tolls[i]);
	}
	//读入记录的数量
	int n;
	scanf("%d",&n);
	//开始读入记录,同一个用户数组下标的第一维得是一样的
	char name[22] = "";
	char time[15] = "";
	char state[10] = "";
	
	for(int i=0;i<n;i++){
		bool hasFound = false;
		scanf("%s %s %s",name,time,state);
		//通过字符串比对姓名,看能否找到他
		for(int j=0;j<row;j++){
			if(strcmp(recs[j][0].name,name)==0){
				strcpy(recs[j][cols[j]].name,name);
				strcpy(recs[j][cols[j]].time,time);
				strcpy(recs[j][cols[j]].state,state);
				cols[j]++;
				hasFound = true;
				break;
			}
		}
		if(!hasFound){//没找到他,新开一列 
			strcpy(recs[row][0].name,name);
			strcpy(recs[row][0].time,time);
			strcpy(recs[row][0].state,state);
			cols[row]++;
			row++;
		} 
	}
	
	//对所有列进行排序
	for(int i=0;i<row;i++){
		sort(recs[i],recs[i]+cols[i],cmp);
	}
	

	
	for(int i=0;i<row;i++){//对于每一个人
//		printf("%s %c%c\\n",recs[i][0].name,recs[i][0].time[0],recs[i][0].time[1]); 
		//赋予姓名 
		strcpy(users[i].name,recs[i][0].name);
		 
		for(int j=0;j<2;j++){
			users[i].month[j]=recs[i][0].time[j];
		}
		double total_money = 0;
		double call_money = 0;
		int call_minute = 0;
		int call_num = 0;
		char startTime[15] = "";
		char endTime[15] = "";
		for(int j=0;j<cols[i]-1;j++){
			if(!strcmp(recs[i][j].state,"on-line")&&!strcmp(recs[i][j+1].state,"off-line")){
				strcpy(startTime,recs[i][j].time);
				strcpy(endTime,recs[i][j+1].time);
				char startTimeExMonth[15] = "";
				for(int i=0;i<8;i++){
					startTimeExMonth[i] = startTime[3+i];
				}
				char endTimeExMonth[15] = "";
				for(int i=0;i<8;i++){
					endTimeExMonth[i] = endTime[3+i];
				}
				
				users[i].calls[call_num].call_minute = getMinute(startTime,endTime);
				users[i].calls[call_num].call_money = getMoney(startTime,endTime,tolls);
				total_money += getMoney(startTime,endTime,tolls);
				strcpy(users[i].calls[call_num].startTimeExMonth,startTimeExMonth);
				strcpy(users[i].calls[call_num].endTimeExMonth,endTimeExMonth);
				call_num++;
				
			}
		}
		users[i].call_num = call_num;
		users[i].total_money = total_money;
	}
	
	//对用户按照总的话费进行排序 
	sort(users,users+row,cmp2);
	
	//输出
	for(int i=0;i<row;i++){
		if(abs(users[i].total_money-0)<eps)continue;
		printf("%s %s\\n",users[i].name,users[i].month);
		for(int j=0;j<users[i].call_num;j++){
			printf("%s %s %d $%.2f\\n",users[i].calls[j].startTimeExMonth,users[i].calls[j].endTimeExMonth,users[i].calls[j].call_minute,users[i].calls[j].call_money);
		}
		printf("Total amount: $%.2f\\n",users[i].total_money);
	} 
	 
	
	
	return 0;
}

以上是关于1016 Phone Bills的主要内容,如果未能解决你的问题,请参考以下文章

PAT 1016. Phone Bills

1016 Phone Bills (25 分)

1016 Phone Bills

1016 Phone Bills

PAT 1016 Phone Bills

PTA-1016——Phone Bills