PAT 1016

题目

给定一天 24 小时的话费价格(注意给定的是一个小时内每分钟的价钱,美分计);然后给出 N 组用户通话记录(包括用户名、通话的时间日期、该时间段通话的状态),每个人的所有记录确保在同一个月内;输出每个人(按照用户名的字母顺序)的月账单

代码和思路

思路

  1. 每个时间段有着不不同的通话单价,所以定义一个数组,从0~23记录每个小时段的通话价格(正好和日常生活规律相同)
  2. 题目中的输入包括了四个时间(月 日 小时 分钟),读入的时候分别读入他们,不要处理成字符串一口气读入,这样读入的时候省事,但是后面处理起来非常麻烦。
  3. 一个用户具有多个相同的属性,所以使用结构体来存储题目数据。
  4. 题目中说所有用户中保证一对输入有效,不代表每个用户有可以输出的东西,所以在计算价格之前一定要统计下这个人有没有匹配的(只要在off之前有一个on就行,因为不可能先off后on,所以先找到一个on,然后去后面找off,只要有就一定成一对儿)。
  5. 题目中说最后的输出按照用户名称的字典序输出,所以一开始就按照字典序排好序,因为on一定在off之前,所以再按照时间排序,这样的话会大大减少后面比较查找的时间。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int max_c = 1010;
int dollar[25];

struct customer {
	char name[25];
	int month, dd, hh, mm;
	bool status;
}cus[max_c], temp;

bool cmp(customer a, customer b) {
	int s = strcmp(a.name, b.name);
	if (s != 0) return s < 0;
	else if (a.month != b.month) return a.month < b.month;
	else if (a.dd != b.dd) return a.dd < b.dd;
	else if (a.hh != b.hh) return a.hh < b.hh;
	else return a.mm < b.mm;
}

void get_ans(int start, int end, int& time, float& money) {
	temp = cus[start];
	while (temp.dd < cus[end].dd || temp.hh < cus[end].hh || temp.mm < cus[end].mm) {
		time++;
		money += dollar[temp.hh];
		temp.mm++;
		if (temp.mm == 60) {
			temp.mm = 0;
			temp.hh++;
		}
		if (temp.hh == 24) {
			temp.hh = 0;
			temp.dd++;
		}
	}
}

int main() {
	for (int i = 0; i < 24; i++) {
		scanf("%d", &dollar[i]);
	}
	int n;
	scanf("%d", &n);
	char line[10];
	for (int i = 0; i < n; i++) {
		scanf("%s", cus[i].name);
		scanf("%d:%d:%d:%d", &cus[i].month, &cus[i].dd, &cus[i].hh, &cus[i].mm);
		scanf("%s", line);
		if (strcmp(line, "on-line") == 0) {
			cus[i].status = true;
		}
		if (strcmp(line, "off-line") == 0) {
			cus[i].status = false;
		}
	}
	sort(cus, cus + n, cmp);
	int start = 0, end, next;
	while (start < n) {
	/*先找该用户能不能成一对,next从当前往后找就行
	有一个on一个off证明一定是有一对儿的,needprint相当于一个flag*/
		int needprint = 0;
		next = start;
		while (next < n && strcmp(cus[next].name, cus[start].name) == 0) {
			if (needprint == 0 && cus[next].status == true) {
				needprint = 1;
			}
			if (needprint == 1 && cus[next].status == false) {
				needprint = 2;
			}
			next++;
		}
		/*next遍历完后,上面跳出循环的条件有一个是名字不同,
		若只有一个用户则这个用户一定有一对儿,若有多个用户则next一定是指向了下一个名字的人
		*/
		if (needprint < 2) {
			start = next;
			continue;
		}
		float AllMoney = 0.0;
		printf("%s %02d\n", cus[start].name, cus[start].month);
		//进入主循环,一定要再找一遍start和end,因为一定是相邻的start和off才是匹配的。
		while (start < next) {
			while (start < next - 1 && !(cus[start + 1].status == false && cus[start].status == true)) {
				start++;
			}
			/*不管找没找到,一定是end = start + 1
			如果匹配成功循环跳出条件为cus[start + 1].status == false && cus[start].status == true,所以
			end = start + 1后end就为与之匹配的off的下标,在后面方便使用。
			如果没有匹配到,则上述循环跳出的条件就变为了start = next - 1,这个时候end = start + 1会使得end
			指向next,这是判断是否这个用户所有对匹配结束的信号
			*/
			end = start + 1;
			if (end == next) {
				start = next;
				break;
			}
			printf("%02d:%02d:%02d ", cus[start].dd, cus[start].hh, cus[start].mm);
			printf("%02d:%02d:%02d ", cus[end].dd, cus[end].hh, cus[end].mm);
			int time = 0;
			float money = 0.0;
			get_ans(start, end, time, money);
			AllMoney += money;
			money = money / 100.0;
			printf("%d $", time);
			printf("%0.2f\n", money);
			start = end + 1; 
		}
		printf("Total amount: $%0.2f\n", AllMoney / 100.0);
	}
	return 0;
}

小结

这个题目本身考察排序函数,因为使用了STL所以排序本身不是问题,问题在于对数据的处理和边界。边界处理好这个题目本身难度就不大了,注意循环主要使用的是while循环且长度长,嵌套多,一定注意循环跳出变量的问题和循环出口问题。

学习到的新东西

  1. 计算两个时间点之间的跨越的时间长度,可以用分钟++,小时++,天数++,边界条件分别就是每小时六十分钟和每天二十四小时
  2. cstring头文件是用于C++中,和C中string.h所具有函数基本一致。
  3. cstring中不能使用运算符,只有长度(strlen()),对比两个是否相等(strcmp()),赋值一个字符串到另一个(strcpy()),把一个字符串借到另一个字符串后面(strcat()),其中参数都是字符数组
  4. break会终止当前循环(不会终止它的外层但是会终止内层)
  5. continue会跳过当前循环下面的所有语句,然后进入循环的下一步。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值