The 13th Shandong ICPC Provincial Collegiate Programming Contest B. Building Company

用类似拓扑排序的写法,先所有能满足要求的项目存入队列。每次从队列中取出一个项目,获得该项目的奖励。然后遍历每个职业,找出所有要求员工数量小于等于当前职业人数的项目,将其要求数减一。如果项目要求数为0,加入队列。

#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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值