用类似拓扑排序的写法,先所有能满足要求的项目存入队列。每次从队列中取出一个项目,获得该项目的奖励。然后遍历每个职业,找出所有要求员工数量小于等于当前职业人数的项目,将其要求数减一。如果项目要求数为,加入队列。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e5 + 7;
// M代表第i个项目的要求数
int M[N], A[N][2];
//K[i]表示完成第i个项目的奖励
vector<PII> K[N];
//mp[i] 是一个按人数排序的小根堆,维护了与工种 i 有关的未满足需求
unordered_map<int, priority_queue<PII, vector<PII>, greater<PII>>> mp;
//公司现有的工种i的员工数量
unordered_map<int, ll> have;
queue<int> q;
void add(int t, int u){
have[t] += u;
priority_queue<PII, vector<PII>, greater<PII>> &res = mp[t];
//找出已经满足工种t数量需求的项目
while (res.size()){
PII tem = res.top();
//res中从小到大排序,遇到第一个大于工种t数量的,那么后面的更不可能合法
if (tem.first > have[t]) break;
res.pop();
//当项目所有要求都被满足时,加入队列
if ((--M[tem.second]) == 0) q.push(tem.second);
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int g; cin >> g;
for (int i = 1; i <= g; i++) cin >> A[i][0] >> A[i][1];
int n; cin >> n;
for (int i = 1; i <= n; i++){
cin >> M[i];
for (int j = 1; j <= M[i]; j++){
int a, b; cin >> a >> b;
mp[a].push({b, i});
}
int k; cin >> k;
for (int j = 1; j <= k; j++){
int c, d; cin >> c >> d;
K[i].push_back({c, d});
}
}
//将没有要求的项目加入队列
for (int i = 1; i <= n; i++) if (M[i] == 0) q.push(i);
//计算公司一开始拥有的员工
for (int i = 1; i <= g; i++) add(A[i][0], A[i][1]);
int ans = 0;
//类似拓扑排序解法,不断取出已经完成的项目,获得奖励
while (q.size()){
int t = q.front();
q.pop();
ans++;
for (auto [x, y] : K[t]) add(x, y);
}
cout << ans;
return 0;
}