洛谷-排隊接水(P1223)

題目描述:

有n個人在一個水龍頭前排隊接水,假如每個人接水的時間爲Ti,請編程找出這n個人排隊的一種順序,使得n個人的平均等待時間最小。
輸入格式 輸入文件共兩行,第一行爲n;第二行分別表示第1個人到第n個人每人的接水時間T1,T2,…,Tn,每個數據之間有1個空格。

輸出格式:

輸出文件有兩行,第一行爲一種排隊順序,即1到n的一種排列;第二行爲這種排列方案下的平均等待時間(輸出結果精確到小數點後兩位)。

輸入輸出樣例 輸入 #1

10
56 12 1 99 1000 234 33 55 99 812

輸出 #1

3 2 7 8 1 4 9 6 10 5
291.90

說明/提示 n<=1000
ti<=1e6,不保證ti不重複
當ti重複時,按照輸入順序即可(sort是可以的)

問題分析:

本題類似進程調度裏的最短作業優先算法。N個人接水,類似就緒隊列中的進程,等待使用CPU。平均等待時間最小,即平均週轉時間最小。不妨以操作系統的角度求解本問題。
回顧下週轉時間:即從進程提交開始,到進程運行結束的這段時間。平均週轉時間呢,即N個進程從被提交到結束的平均時間。
最段作業優先算法,每次選取進程運行時間最少的開始執行。該算法典型的貪心算法,每次選取運行時間最短的,毫無疑問,最終結果一定是最優解(每次選取時間最短的優先運行,即保證其他進程的週轉時間相對較短)。

算法步驟:

  1. 對運行時間進行排序,目的是保證每個進程的週轉時間最短(即每個人的等待時間最短)。
  2. 週轉時間的計算,根據週轉時間定義得知:我的週轉時間包括前邊的各進程週轉時間(第N個人打水時間,包括前N-1個人的時間)
  3. 各進程的週轉時間相加,再做一次平均運算,即最終結果。

代碼示例:

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

struct Person{
 	int num;
 	int time;
}que[1010]; 

bool cmp(Person x, Person y){
 	return x.time < y.time;
}

int main(){
 	int n;
 	long sum = 0;
  	scanf("%d", &n);
 	for(int i = 0; i < n; i++){
  		scanf("%d", &que[i].time);
  		que[i].num = i+1;
 	}
 	sort(que, que+n, cmp);
 	for(int i = 0; i < n; i++){
  		printf("%d ", que[i].num);
  		sum += (n-i-1) * que[i].time;
 	}
 	printf("\n%.2lf", sum / (n * 1.0));
 	return 0;
} 

雖然運行時間是整數, 但是計算總時間時需要注意整型範圍溢出,因此不妨將總時間的數值類型開的更大一點。

總結:

求解本問題時,一直以最短作業優先算法的思維做這題。但是測試一直不過。原因在於,本題不需要將自己的執行時間計入在總時間裏(自己在打水時間沒有被算入)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章