2023 山东 I C P C 省赛 P r o b l e m B . 建筑公司 \Huge{2023山东ICPC省赛Problem B.建筑公司} 2023山东ICPC省赛ProblemB.建筑公司
文章目录
- 题意
- 思路
- 标程
比赛链接:Dashboard - The 13th Shandong ICPC Provincial Collegiate Programming Contest - Codeforces
官方题解:B - 建筑公司 - SUA Wiki
题意
题目给出若干中工种和每类工种的人数,然后给出若干项工程,每项工程的性质有:
- 完成该项工程所需的各工种人数。
- 完成该项工程后可以增加的每类工种人数。要求求出最多能够完成多少项工程?
每项工程需要完成的时间忽略不计,可以理解为只要各工种人数满足该工程所需人数,则和获取此工程的增加人数,并且需要完成该工程的人数不会消耗。
思路
我们可以简化题目,看作每项工程只需一类工种,那么自然很容易想到拓扑排序。
但是这道题稍微复杂一些,每项工程需要的工种种类多一些。
我们考虑对于每项工程建立拓扑图,并且把每项工程所需的各工种人数看作是连接在工程上的节点,若现有工种人数大于该工程的所需人数,则将该工种所代表的节点从该工程上去掉;若某工程的入度为 0 0 0,则表示该工程所有需要的工人数量都满足,那么就可以完成该工程,并且将该工程可增加的工种人数加上。
需要注意的是:本题的重点是如何删除每个工程上的节点(工程需要的工种人数)?
- 可行的做法是:可以将每个工种数量不满足的工程给存起来然后排序。
- 具体排序规则为:将需要该工种的工程根据需要的数量从小到大排序。
- 容易知道,若当前度不为 0 0 0的节点,只有其工种人数增加后,该节点才可能被消除。
注意点已详细注释在标程代码中,以便理解。
标程
#include<bits/stdc++.h>using namespace std;#define IOS ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
#define LL long long
#define ULL unsigned long long
#define PII pair<int, int>
#define lowbit(x) (x & -x)
#define Mid ((l + r) >> 1)
#define ALL(x) x.begin(), x.end()
#define endl '\n'
#define fi first
#define se secondconst int INF = 0x7fffffff;
const int Mod = 1e9 + 7;
const int N = 2e5 + 10;int g, n, k, m, t, u;
map<int, int> a;//a存每个工种的人数
map<int, priority_queue<PII, vector<PII>, greater<PII>>> q;//存每个工种数量不满足的工程
vector<PII> v[N];//v存每个工程可增加的每个工种人数
vector<int> c(N);//c存每个工程缺的工种数量
queue<int> qu;//存当前入度为0的工程void Solved() {cin >> g;for(int i = 1; i <= g; i ++ ) {//记录各工种人数cin >> t >> u;a[t] = u;}cin >> n;for(int i = 1; i <= n; i ++ ) {cin >> m;for(int j = 1; j <= m; j ++ ) {//每项工程需要人数cin >> t >> u;if(a[t] < u) { //只保存还需要的人数就行(找出初始入度为0的工程)c[i] ++;q[t].push({u, i});}}cin >> k;for(int j = 1; j <= k; j ++ ) {//每项工程增加人数cin >> t >> u;v[i].push_back({t, u});}}for(int i = 1; i <= n; i ++ ) {//先将入度为0的工程处理掉if(!c[i]) qu.push(i);}int res = 0;//q[i]表示对于第i种工人人数,不满足哪些工程//qu中存放当前入度为0的工程while(!qu.empty()) {int i = qu.front(); qu.pop();res ++;//对于每个工种,只有其数量增加,才能完成其他工程,所以只需要判断增加的工种即可for(auto f : v[i]) {t = f.fi, u = f.se;a[t] += u; //添加当前工程可增加的各工种人数while(!q[t].empty()) {//判断当前所有需要工种t的工程PII p = q[t].top();if(a[t] >= p.fi) {c[p.se] --;q[t].pop();//若当前工程需要的人都满足,则该工程入度为0if(c[p.se] == 0) qu.push(p.se);} else {//对于当前工种,若工程不满足,则先跳过break;}}}}cout << res << endl;
}signed main(void) {IOSint ALL = 1; // cin >> ALL;while(ALL -- ) Solved();// cout << fixed;//强制以小数形式显示// cout << setprecision(n); //保留n位小数return 0;
}