題目:
排隊“夾塞”是引起大家強烈不滿的行為,但是這種現象時常存在。在銀行的單窗口排隊問題中,假設銀行只有1個窗口提供服務,所有顧客按到達時間排成一條長龍。當窗口空閑時,下一位顧客即去該窗口處理事務。此時如果已知第i位顧客與排在后面的第j位顧客是好朋友,並且願意替朋友辦理事務的話,那么第i位顧客的事務處理時間就是自己的事務加朋友的事務所耗時間的總和。在這種情況下,顧客的等待時間就可能被影響。假設所有人到達銀行時,若沒有空窗口,都會請求排在最前面的朋友幫忙(包括正在窗口接受服務的朋友);當有不止一位朋友請求某位顧客幫忙時,該顧客會根據自己朋友請求的順序來依次處理事務。試編寫程序模擬這種現象,並計算顧客的平均等待時間。
輸入格式:
輸入的第一行是兩個整數:1≤N≤10000,為顧客總數;0≤M≤100,為彼此不相交的朋友圈子個數。若M非0,則此后M行,每行先給出正整數2≤L≤100,代表該圈子里朋友的總數,隨后給出該朋友圈里的L位朋友的名字。名字由3個大寫英文字母組成,名字間用1個空格分隔。最后N行給出N位顧客的姓名、到達時間T和事務處理時間P(以分鍾為單位),之間用1個空格分隔。簡單起見,這里假設顧客信息是按照到達時間先后順序給出的(有並列時間的按照給出順序排隊),並且假設每個事務最多占用窗口服務60分鍾(如果超過則按60分鍾計算)。
輸出格式:
按顧客接受服務的順序輸出顧客名字,每個名字占1行。最后一行輸出所有顧客的平均等待時間,保留到小數點后1位。
思路:
題目整體的思路並不是很難,但需要注意的細節有點多。
1、對於每個朋友圈,我們可以對每一個客戶進行編號,然后用並查集來儲存每一個朋友圈。
2、首先讓第一個到達的客戶進行處理業務,對之后的客戶分兩個情況處理
(1)后邊在當前客戶的業務處理之前,有加塞客戶到來,依次處理這些加塞客戶
(2)沒有加塞客戶到來的話,就去處理按正常順序來的下一個客戶,然后循環步驟2
3、每處理一個客戶相應的就更新下窗口當前業務的結束時間和當前窗口處理的客戶的編號。
代碼:

#include <bits/stdc++.h> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn = 1e4+10; struct Peo { string name; int arrive; int timeNeed; int waitTime; bool vis; } p[maxn]; map<string, int> mp; vector<string> v[110]; queue<Peo> que; int m,n,k; int pre[maxn]; void init(){ for(int i = 0; i<110; i++) v[i].clear(); while(!que.empty()) que.pop(); for(int i = 0; i<maxn; i++){//對每一個客戶進行編號,后邊並查集存朋友圈會用到 pre[i] = i; } } int Find(int x){ return x==pre[x] ? x : pre[x] = Find(pre[x]); } void putIn(){ string name; cin>>m>>n; for(int i = 0; i<n; i++){//輸入朋友圈 cin>>k; for(int j = 0; j<k; j++){ cin>>name; v[i].push_back(name); } } for(int i = 0; i<m; i++){//輸入每一個客戶的有關信息 cin>>p[i].name>>p[i].arrive>>p[i].timeNeed; if(p[i].timeNeed>60) p[i].timeNeed = 60; p[i].waitTime = 0; p[i].vis = false; mp[p[i].name] = i; } for(int i = 0; i<n; i++){//對給出的朋友圈,用並查集儲存 int tx = Find(mp[v[i][0]]); for(int j = 1; j<v[i].size(); j++){ int ty = Find(mp[v[i][j]]); if(tx!=ty) pre[ty] = tx; } } } void solve(){ int endTime = p[0].arrive+p[0].timeNeed,curId = 0;//endTime是窗口當前業務結束的時間,curId是當前正在 p[0].vis = true; //處理業務的客戶 p[0].waitTime = 0; que.push(p[0]); while(1){ bool isFind = false; for(int i = 1; i<m; i++){//處理加塞的人(即與當前所有與正在處理業務的客戶在一個朋友圈的人) int tx = Find(i),ty = Find(curId); if(tx==ty && !p[i].vis && p[i].arrive <= endTime){//這個客戶必須沒有訪問過,而且是在他朋友結束前到來 p[i].vis = true; que.push(p[i]);//壓入答案隊列 p[i].waitTime = endTime-p[i].arrive;//更新這個客戶的等待時間 endTime = endTime+p[i].timeNeed;//更新窗口的當前業務的結束時間 curId = i;//更新窗口當前客戶的id isFind = true;//說明有加塞的客戶出現 } } if(!isFind){//沒有加塞的客戶的話,就去處理其余的客戶 for(int i = 1; i<m; i++){ int tx = Find(i),ty = Find(curId); if(tx!=ty && !p[i].vis){ p[i].vis = true; que.push(p[i]); if(p[i].arrive<=endTime){//根據這個客戶到達時間的不同分別更新客戶的等待時間 p[i].waitTime = endTime-p[i].arrive;//窗口的結束時間 endTime = endTime+p[i].timeNeed; curId = i; } else{ p[i].waitTime = 0; endTime = p[i].arrive+p[i].timeNeed; curId = i; } break;//這種不加塞的客戶只要找到一個就可以了,然后進入下一個循環去處理與這個客戶有關的加塞客戶 } } } if(que.size() == m) break; } while(!que.empty()){ cout<<que.front().name<<endl; que.pop(); } double sum = 0; for(int i = 0; i<m; i++){//遍歷所有的客戶,求等待時間的和,求平均值 sum += p[i].waitTime; } printf("%.1f\n",sum/m); return; } int main() { init(); putIn(); solve(); return 0; } /* putIn: 6 2 3 ANN BOB JOE 2 JIM ZOE JIM 0 20 BOB 0 15 ANN 0 30 AMY 0 2 ZOE 1 61 JOE 3 10 putOut: JIM ZOE BOB ANN JOE AMY 75.2 */