1. 程式人生 > 其它 >P228【基礎】排隊打水問題 題解

P228【基礎】排隊打水問題 題解

題意

有n個人排隊到r個水龍頭去打水,他們裝滿水桶的時間t1,t2,...,tn為整數且各不相等,應如何安排他們的打水順序才能使他們花費的總時間最少?

每個人打水的時間 = 排隊的時間 + 實際打水的時間,本題假設一個人打好水,排在他後面的人接著打水的這個切換過程不消耗時間。

比如,有2個人A和B,他們打水的時間分別是3和2,只有1個水龍頭,這時,如果A先打水,B後打水,那麼A和B打水的時間分別為3、3+2(B排隊3分鐘)。

因此,所有人打水的總時間就是每個人的打水時間及每個人的排隊時間的總和。

輸入格式

第1行,兩個整數n(1<=n<=500)和r(1<=r<=100)。

第2行,n個正整數t1,t2,...,tn,(1<=ti<=1000)表示每個人裝滿水桶的時間。


輸出格式

1行,一個正整數,表示他們花費的最少總時間。

樣例輸入

 4 2
 2 6 4 5

樣例輸出

 23

題解

運用優先佇列 + 貪心演算法

花費的時間=每個人接水的時間+每個人排隊等待的時間

因為每個人的接水時間已經是固定的了,那麼要確定最少的花費時間

就取決於讓每個人排隊等待的時間最少

這樣讓接水時間少的人先接,就能保證後面排隊的人用時最短

程式碼

#include <bits/stdc++.h>
using namespace std;
const int N = 110;

priority_queue<int, vector<int>, greater<int> > q;  //小根堆,隊頭總是為最小元素
vector<int> a[N]; 	 

int main()
{
 int n, r, sum_do = 0, sum_wait = 0;
 cin >> n >> r;
 
 for(int i = 1, x; i <= n; i++)  //先計算所有人的打水時間,並壓入小根堆(優先佇列)
 {
 	cin >> x;
 	sum_do += x;
 	q.push(x);
 }
 	
 int t = 1;
 for(int i = 1; i <= n; i++)   //再分別讓最小值迴圈插入r個水龍頭
 {
 	a[t].push_back(q.top());
 	q.pop();
 	t = t % r + 1;   //該行可構成迴圈插入
 }	
 
 
 for(int i = 1; i <= r; i++)
 {
 	int temp = 0;
 	for(int j = 0; j < a[i].size() - 1; j++)  //除了每個水龍頭的最後一個人,其他的等待時間都要加上
 	{
 		temp += a[i][j];    //逐個加上
 		sum_wait += temp;
 	}
 }
 	
 cout << sum_do + sum_wait;  //總時間 = 打水時間 + 等待時間
 return 0;
}