解題思路
題面中提到後面的人可以找前面的是朋友的人夾塞,讓朋友幫忙辦理業務,朋友的辦理時間會累加。這個過程可以看作一個插隊的過程,即把自己插到朋友的後面。由于朋友幫自己辦業務是緊接着他自己剛辦完的業務連續辦理的,是以自己插在朋友後面和讓朋友累加辦理時間是等價的。
是以我們可以使用隊列模拟,每次 pop 出隊首,表示此人已辦理完畢。接下來找一個人放到隊列中作為下一個要辦理的人。而下一個的人選是有要求的,即優先尋找一個到達時間不晚于自己離開時間的朋友,讓他排在自己後面(插隊),如果找不到則按原順序找自己後面的第一個人,讓他排在自己後面。這樣不斷循環并累加等待時間,直到隊列為空即可。
參考代碼
#include <cstdio>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
using namespace std;
struct info {
char name[];
int t;
int p;
} a[];
int Hash(char *s) { // 把字元串哈希成數字,防止逾時
return (s[]-'A')** + (s[]-'0')* + s[]-'0';
}
int main(int argc, char const *argv[]) {
int n, m, l;
char s[];
bool vis[] = {false}; // 标記是否已辦理完畢
scanf("%d %d", &n, &m);
set<int> st[]; // 朋友圈集合
map<int, int> mp; // 映射人名到朋友圈編号
for(int i=; i<m; ++i) {
scanf("%d", &l);
while(l--) {
scanf("%s", s);
st[i].insert(Hash(s));
mp[Hash(s)] = i;
}
}
for(int i=; i<n; ++i) {
scanf("%s %d %d", a[i].name, &a[i].t, &a[i].p);
if(a[i].p > ) a[i].p = ;
}
queue<info> q;
q.push(a[]);
vis[] = true;
int last = a[].t+a[].p;
int sum = ;
while(!q.empty()) {
info f = q.front();
q.pop();
printf("%s\n", f.name);
bool found = false;
for(int i=; i<n; ++i) { // 尋找一個到達時間不晚于自己辦理結束時間的朋友
if(vis[i]) continue;
if(a[i].t > last) break;
if(st[mp[Hash(f.name)]].count(Hash(a[i].name))) {
q.push(a[i]);
vis[i] = true;
found = true;
sum += last-a[i].t;
last += a[i].p;
break;
}
}
if(!found) { // 未找到,則按原順序找下一個人
for(int i=; i<n; ++i) {
if(vis[i]) continue;
q.push(a[i]);
vis[i] = true;
sum += max(, last-a[i].t); // 等待時間可能為 0(視窗空閑一段時間)
if(a[i].t > last) last = a[i].t;
last += a[i].p;
break;
}
}
}
printf("%.1f\n", *sum/n);
return ;
}