1. 程式人生 > 其它 >1015 德才論

1015 德才論

我們是 冠軍!

一開始報段錯誤

後來報超時

最後報錯

這道題屬實是給我折磨完了

先貼程式碼,最後總結

1015 德才論 (25 分)

宋代史學家司馬光在《資治通鑑》中有一段著名的“德才論”:“是故才德全盡謂之聖人,才德兼亡謂之愚人,德勝才謂之君子,才勝德謂之小人。凡取人之術,苟不得聖人,君子而與之,與其得小人,不若得愚人。”

現給出一批考生的德才分數,請根據司馬光的理論給出錄取排名。

輸入格式:

輸入第一行給出 3 個正整數,分別為:N(≤105),即考生總數;L(≥60),為錄取最低分數線,即德分和才分均不低於 L 的考生才有資格被考慮錄取;H(<100),為優先錄取線——德分和才分均不低於此線的被定義為“才德全盡”,此類考生按德才總分從高到低排序;才分不到但德分到優先錄取線的一類考生屬於“德勝才”,也按總分排序,但排在第一類考生之後;德才分均低於 H

,但是德分不低於才分的考生屬於“才德兼亡”但尚有“德勝才”者,按總分排序,但排在第二類考生之後;其他達到最低線 L 的考生也按總分排序,但排在第三類考生之後。

隨後 N 行,每行給出一位考生的資訊,包括:准考證號 德分 才分,其中准考證號為 8 位整數,德才分為區間 [0, 100] 內的整數。數字間以空格分隔。

輸出格式:

輸出第一行首先給出達到最低分數線的考生人數 M,隨後 M 行,每行按照輸入格式輸出一位考生的資訊,考生按輸入中說明的規則從高到低排序。當某類考生中有多人總分相同時,按其德分降序排列;若德分也並列,則按准考證號的升序輸出。

輸入樣例:

14 60 80
10000001 64 90
10000002 90 60
10000011 85 80
10000003 85 80
10000004 80 85
10000005 82 77
10000006 83 76
10000007 90 78
10000008 75 79
10000009 59 90
10000010 88 45
10000012 80 100
10000013 90 99
10000014 66 60

輸出樣例:

12
10000013 90 99
10000012 80 100
10000003 85 80
10000011 85 80
10000004 80 85
10000007 90 78
10000006 83 76
10000005 82 77
10000002 90 60
10000014 66 60
10000008 75 79
10000001 64 90

題解:

#include<stdio.h>
typedef struct student
{
	long long num;
	int d;
	int c;
}stu;

void print(stu* s)
{
	for (int i = 0; s[i].num != 0; i++)
	{
		printf("%d %d %d\n", s[i].num, s[i].d, s[i].c);
	}
}

int cmp(const void* a, const void* b)
{
	stu* c = (stu*)a;
	stu* d = (stu*)b;
	int sum1 = c->c + c->d;
	int sum2 = d->c + d->d;
	if (sum1 != sum2)
	{
		return sum2 - sum1;
	}
	else if (c->d != d->d)
	{
		return d->d - c->d;
	}

	else return c->num - d->num;
}

int main()
{
	int out1 = 0, out2 = 0, out3 = 0, out4 = 0;
	int sum;
	int L;
	int H;//優先錄取線
	scanf("%d %d %d", &sum, &L, &H);
	static stu student[100000];
	static stu student1[100000] = { 0 };
	static stu student2[100000] = { 0 };
	static stu student3[100000] = { 0 };
	static stu student4[100000] = { 0 };

	for (int i = 0; i < sum; i++)
	{
		scanf("%d", &student[i].num);
		scanf("%d", &student[i].d);
		scanf("%d", &student[i].c);
	}

	for (int i = 0; i < sum; i++)
	{
		if (student[i].d >= H && student[i].c >= H)
		{
			student1[out1] = student[i];
			out1++;
		}
		else if (student[i].d >= H && student[i].c < H && student[i].c >= L)
		{
			student2[out2] = student[i];
			out2++;
		}
		else if (student[i].d < H && student[i].c < H && student[i].d >= student[i].c && student[i].d >= L && student[i].c >= L)
		{
			student3[out3] = student[i];
			out3++;
		}
		else if (student[i].d >= L && student[i].c >= L)
		{
			student4[out4] = student[i];
			out4++;
		}
	}
	
	//快速排序
	
	qsort(student1, out1, sizeof(stu), cmp);
	qsort(student2, out2, sizeof(stu), cmp);
	qsort(student3, out3, sizeof(stu), cmp);
	qsort(student4, out4, sizeof(stu), cmp);

	printf("%d\n", out1 + out2 + out3 + out4);
	print(student1);
	print(student2);
	print(student3);
	print(student4);
}

總結:

下面依次總結所有問題:

  1. 段錯誤。顯然是陣列的越界問題,但是題目要求就是100000,沒辦法啊,答案是在VS中,使用static設定為全域性變數,記憶體就夠了。

  2. 超時。可以優化的地方太多了,而且我現在的寫法依舊有優化的空間

    1.先說大問題,一開始我是手寫的氣泡排序,每讀取一個就插入一個,時間複雜度n^2,雖然很清楚明瞭,但是顯然是可以優化成nlogn的。而c語言中有自帶的快速排序函式qsort(),在此我沒有深入研究,因為資料結構還沒看到那裡呢,總之就是可以優化。

    2.剛開始改成快速排序,由於還是要把資料分為四大類,我還是每次遍歷陣列直到資料不為空,然後插入新的資料。我尋思陣列的優勢不就是隨機讀寫嗎,只需要每次插入後計數,就可以每次精準的插到最後。之前每次遍歷都需要一個o(n),現在直接優化成o(1)了。

    3.這點可忽略,之前氣泡排序的時候每次還遍歷一次陣列求長度,屬於是逆天

  3. 答案錯誤。優化了半天,終於不超時,改幾把報錯了。

    問題很簡單,構造cmp函式時,搞錯了總分相等時的情況,改下就好了。

可以優化的地方還很多,但我還要tm背英語,就這樣了,這題屬於是收穫頗多了!

參考文章

static陣列 https://blog.csdn.net/weixin_34923852/article/details/117116675
程式碼和我類似,這兄弟也是參考的別人的,我屬於三手程式碼 https://blog.csdn.net/YelloJesse/article/details/82377704
還有個兄弟提醒我不用每次遍歷,直接計數插入就可以了,找不到了