1075 PAT Judge (25分)/细节/神坑题

题目链接

题目本身不是很难,只是结构体的排序。但是包含细节比较多,需要做几件事:
(1)每道题需要判断考生是否编译成功,或者未提交,未提交和无法通过编译都是0分;
(2)如果某考生完全未提交或者所有题都无法通过编译,那么不需要输出(但会参与排名)。
(3)将考生排序(按总分、得满分的题数、id号顺序),并输出rank,分数相同的考生rank相同


分析

那么本题的主要问题就是如何表示未提交和未通过编译。由于未通过编译的情况下,输入分数为-1,通过编译分为为0~range,我的想法是初始时每题的分数设为-2。判断输入分数是否大于当前该题的分数,若大于就覆盖。由于小于等于的情况不会通过条件判断,其他博主遇到的“神坑”——(1)某考生多次提交满分导致程序求得的满分个数出错, (2)某考生先得了分,然后再提交未通过编译,这些神坑都不会成为我的程序的BUG
可是我的程序也遇到了奇妙的事,无法通过最后一个测试点,对,只是最后一个!!我对着程序看了几遍,虽然刚开始确实有怀疑过我对rank的求法,但也没觉得哪里错了,也没有试着去改,就把程序其他地方这里改改那里改改。
眼见还是没办法AC,我的心开始焦灼,于是学柳神的,把rank放到了结构体里面。于是就AC了???可是为什么呢??

#include<iostream>
#include<algorithm>
using namespace std;
struct stu {
	int score[6] = { -2,-2,-2,-2,-2,-2 }, total, id, num, flag,rank;
}s[10005];
bool cmp(stu &a, stu &b) {
	if (a.total != b.total) return a.total > b.total;
	else if (a.num != b.num) return a.num > b.num;
	else return a.id < b.id;
}
int p[6];
int main() {
	int n, k, m;
	cin >> n >> k >> m;
	for (int i = 1; i <= k; i++) {
		cin >> p[i];
	}
	while (m--) {
		int t_id, t_num, t_score;
		scanf("%d%d%d", &t_id, &t_num, &t_score);
		s[t_id].id = t_id;
		if (t_score > s[t_id].score[t_num]) {
			if (t_score >= 0) s[t_id].flag = 1;
			//可以用在线处理,条件判断会比较麻烦,不建议
			//if (t_score == p[t_num]) s[t_id].num++;
			//if (s[t_id].score[t_num] == -1|| (s[t_id].score[t_num] == -2&&t_score!=-1)) s[t_id].total += t_score;
			//else if(s[t_id].score[t_num] != -2 && t_score != -1) s[t_id].total = s[t_id].total - s[t_id].score[t_num] + t_score;
			s[t_id].score[t_num] = t_score;
		}
	}
	//由于k<=5,即使是两层循环,也不会需要太多时间。否则还是得用在线处理
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= k; j++) {
			if (s[i].score[j] != -1 && s[i].score[j] != -2) s[i].total += s[i].score[j];
			if (s[i].score[j] == p[j]) s[i].num++;
		}
	}
	sort(s + 1, s + n + 1, cmp);
	for (int i = 1; i <= n; i++) {
		if (i == 1) s[i].rank = 1;
		else {
			if (s[i].total == s[i - 1].total) s[i].rank = s[i - 1].rank;
			else s[i].rank = i;
		}
	}
	//int rank = 1;
	for (int i = 1; i <= n; i++) {
		if (s[i].flag == 1) {
			/*if (i != 1 && s[i].total < s[i - 1].total) {  //开始是这么求rank的
				rank = i;
			}*/
			printf("%d %05d %d", s[i].rank, s[i].id, s[i].total);
			for (int j = 1; j <= k; j++) {
				if (s[i].score[j] != -2) {
					if (s[i].score[j] != -1) printf(" %d", s[i].score[j]);
					else printf(" 0");
				}
				else printf(" -");
			}
			printf("\n");
		}		
	}
	return 0;
}

一番思考之后,我算是明白了:照题目的意思,完全未提交和均未通过编译这两种情况下,考生总分都是0,且如果有人并不是这两种情况,但是总分也只有0分,那么照我之前求rank的方法,rank就会出错。比如(以下格式是 ID total flag rank(flag==true表示需要输出)):
A:00519 1 1 519
B:00520 0 0 /
C:00521 0 1 521
那么照开始时求rank的方法, i == 520 时,由于不满足if语句,会进行下一次循环,由于B.total == C.total,于是直接输出当前的rank,但此时rank的值是519,但实际上C的rank应该是520才对。于是错误产生了。


一种避免以上错误的方法

把需要输出的另外保存到vector里,再sort,求rank。

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct stu {
	int score[6] = { -2,-2,-2,-2,-2,-2 }, total, id, num, flag;
}s[10005];
bool cmp(stu &a, stu &b) {
	if (a.total != b.total) return a.total > b.total;
	else if (a.num != b.num) return a.num > b.num;
	else return a.id < b.id;
}
int p[6];
int main() {
	int n, k, m;
	cin >> n >> k >> m;
	for (int i = 1; i <= k; i++) {
		cin >> p[i];
	}
	while (m--) {
		int t_id, t_num, t_score;
		scanf("%d%d%d", &t_id, &t_num, &t_score);
		s[t_id].id = t_id;
		if (t_score > s[t_id].score[t_num]) {
			if (t_score >= 0) s[t_id].flag = 1;
			s[t_id].score[t_num] = t_score;
		}
	}
	vector<stu> v;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= k; j++) {
			if (s[i].score[j] != -1 && s[i].score[j] != -2) s[i].total += s[i].score[j];
			if (s[i].score[j] == p[j]) s[i].num++;
		}
		if (s[i].flag == 1) v.push_back(s[i]);
	}

	//sort(s + 1, s + n + 1, cmp);
	sort(v.begin(), v.end(),cmp);
	int rank = 1;
	for (int i = 0; i < v.size(); i++) {
		if (i != 0 && v[i].total < v[i - 1].total) rank = i+1;
		printf("%d %05d %d", rank, v[i].id, v[i].total);
		for (int j = 1; j <= k; j++) {
			if (v[i].score[j] != -2) {
				if (v[i].score[j] != -1) printf(" %d", v[i].score[j]);
				else printf(" 0");
			}
			else printf(" -");
		}
		printf("\n");
	}
	return 0;
}

原程序的修改

那么如何把最开始的程序修改成可以AC的代码,可以先求rank,再根据flag判断是否输出。


#include<algorithm>
using namespace std;
struct stu {
	int score[6] = { -2,-2,-2,-2,-2,-2 }, total, id, num, flag;
}s[10005];
bool cmp(stu &a, stu &b) {
	if (a.total != b.total) return a.total > b.total;
	else if (a.num != b.num) return a.num > b.num;
	else return a.id < b.id;
}
int p[6];
int main() {
	int n, k, m;
	cin >> n >> k >> m;
	for (int i = 1; i <= k; i++) {
		cin >> p[i];
	}
	while (m--) {
		int t_id, t_num, t_score;
		scanf("%d%d%d", &t_id, &t_num, &t_score);
		s[t_id].id = t_id;
		if (t_score > s[t_id].score[t_num]) {
			if (t_score >= 0) s[t_id].flag = 1;
			s[t_id].score[t_num] = t_score;
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= k; j++) {
			if (s[i].score[j] != -1 && s[i].score[j] != -2) s[i].total += s[i].score[j];
			if (s[i].score[j] == p[j]) s[i].num++;
		}
	}
	sort(s + 1, s + n + 1, cmp);
	int rank = 1;
	for (int i = 1; i <= n; i++) {
		if (i != 1 && s[i].total < s[i - 1].total) {
			rank = i;
		}
		if (s[i].flag == 0) continue;  //先求rank,再判断
		printf("%d %05d %d", rank, s[i].id, s[i].total);
		for (int j = 1; j <= k; j++) {
			if (s[i].score[j] != -2) {
				if (s[i].score[j] != -1) printf(" %d", s[i].score[j]);
				else printf(" 0");
			}
			else printf(" -");
		}
		printf("\n");
		
	}
	return 0;
}

感谢

有时候思路有并不代表你会写代码,会写代码并不一定代表你能写对,能写对并不一定代表你能力强因为你可能需要大量时间去改BUG。但是,努力总不会白费的。

发布了62 篇原创文章 · 获赞 7 · 访问量 3133

猜你喜欢

转载自blog.csdn.net/weixin_43590232/article/details/104107865