PAT A1016 Phone Bills 简单方法和出错分析

题目链接
《算法笔记》上面给的代码在最后查找那一块结构有点混乱,臃肿,稍显复杂,其实完全没有必要。
书上的参考代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1010;
int toll[25];
struct Rec{
	char name[25];
	int month,dd,hh,mm;
	bool status;
}rec[maxn],temp;
bool cmp(Rec a, Rec b){
	int s=strcmp(a.name,b.name);
	if (s) 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 on, int off, int& time, int& money){
	temp=rec[on];
	while (temp.dd<rec[off].dd || temp.hh<rec[off].hh || temp.mm<rec[off].mm){
		time++;
		money+=toll[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",&toll[i]);
	}
	int n;
	scanf("%d",&n);
	char line[10];
	for (int i=0; i<n; i++){
		scanf("%s",rec[i].name);
		scanf("%d:%d:%d:%d",&rec[i].month,&rec[i].dd,&rec[i].hh,&rec[i].mm);
		scanf("%s",line);
		if (strcmp(line,"on-line")==0){
			rec[i].status=true;
		}else {
			rec[i].status=false;
		}
	}
	sort(rec,rec+n, cmp);
	int on=0,off,next;
	while (on<n){
		int needPrint=0;
		next=on;
		while (next<n && strcmp(rec[next].name,rec[on].name)==0){
			if (needPrint==0 && rec[next].status==true){
				needPrint=1;
			}else if (needPrint==1 && rec[next].status==false){
				needPrint=2;
			}
			next++;
		} 
		if (needPrint<2){
			on=next;
			continue;
		}
		int AllMoney=0;
		printf("%s %02d\n",rec[on].name,rec[on].month);
		while (on<next){
			while(on<next-1 && !(rec[on].status==true && rec[on+1].status==false)){
				on++;
			}
			off=on+1;
			if (off==next){
				on=next;
				break;
			}
			printf("%02d:%02d:%02d ",rec[on].dd,rec[on].hh,rec[on].mm);
			printf("%02d:%02d:%02d ",rec[off].dd,rec[off].hh,rec[off].mm);
			int time=0,money=0;
			get_ans(on,off,time,money);
			AllMoney+=money;
			printf("%d $%.2f\n",time,money/100.0);
			on=off+1;
		}
		printf("Total amount: $%.2f\n",AllMoney/100.0);
	} 
}

总共有90多行,其中一半都用来查找输出,好几个continue,结构混乱不容易理解,作者的想法是很好的,但实现的代码就太臃肿了,归根结底,我们就是要在数组排序完成以后,查找相同名字下的相邻on和off,这本来可以很简单。
我的代码如下:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int toll[24];
struct Rec{
	char name[25];
	int mon,dd,hh,mm;
	int isOn;
}rec[1010];
bool cmp(Rec a, Rec b){
	if (strcmp(a.name, b.name)) return strcmp(a.name, b.name)<0;
    else if (a.mon!=b.mon) return a.mon<b.mon;
	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;
}
int  getAmount(Rec a, Rec b, double& amount){
	int min=0;
	while (a.dd!=b.dd || a.hh!=b.hh || a.mm!=b.mm){
		amount+=toll[a.hh];
		a.mm++;
		min++;
		if (a.mm==60){
			a.hh++;
			a.mm=0;
		}
		if (a.hh==24){
			a.dd++;
			a.hh=0;
		}
	}
	return min;
}
int main(){
	for (int i=0; i<24; i++){
		scanf("%d",&toll[i]);
	}
	int n;
	scanf("%d",&n);
	for (int i=0; i<n; i++){
		char temp[20];
		scanf("%s %d:%d:%d:%d %s",rec[i].name,&rec[i].mon,&rec[i].dd,&rec[i].hh,&rec[i].mm,temp);
		if (strcmp(temp,"on-line")) rec[i].isOn=0;
		else rec[i].isOn=1;
	}
	sort(rec,rec+n,cmp);
	int flag=0,min=0;
	double amount=0,all=0;
	for (int i=0; i<n-1; i++){
		if (strcmp(rec[i].name,rec[i+1].name)==0){
			if (rec[i].isOn && !rec[i+1].isOn){
				if (!flag) {
					printf("%s %02d\n",rec[i].name,rec[i].mon);
					flag=1;
				}
				amount=0;
				min=getAmount(rec[i],rec[i+1],amount);
				printf("%02d:%02d:%02d %02d:%02d:%02d %d $%.2f\n",rec[i].dd,rec[i].hh,rec[i].mm,
				rec[i+1].dd,rec[i+1].hh,rec[i+1].mm,min,amount/100);
				all+=amount/100;	
			}
		}else {
			if (flag){
				printf("Total amount: $%.2f\n",all);
			}
			flag=all=amount=min=0;
		}
	}
	if (flag){
		printf("Total amount: $%.2f",all);
	}
}

代码长度和时间性能上面都更好。

这个题目有一个有点坑爹的地方,他的样例数据给的人名都很短,但是他的测试点1,2,3给的人名都是题目中限定的最长长度。

在调试过程中,一开始只有第一个测试点能通过,但我确定逻辑是没有问题的,最后发现问题在于我把结构体里面的字符串数组长度定为20,太小了(虽然题目上确实说给出的名字长度不超过20个字符,但如果恰恰是20个字符,那么数组中就没有 \0 了,而测试点123恰恰都是最长长度),改成25就好了。还是要仔细看题目里面的数据长度,然后设定的数据长度一定要比题目大才行,和题目相等都不行,在这里卡住就实在是太倒霉了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值