NC20951 网络优化

题目链接

题目

题目描述

《梦三国2》是一款3D MOBA类网游。游戏继承《梦三国》的三国文化背景和基础玩法,并加入许多全新地图和全新竞技玩法。由于人气高,游戏在线人数与日俱增,我们知道当在线人数不断增长的时候,会给服务器带来巨大的压力。
已知该游戏中共有n名用户,编号从1到n,服务器共有m条服务线,每个用户最多只能登陆一条线,第i条线最多可以容纳v[i]名用户同时在线,且只能给编号在[l[i],r[i]]范围内的用户提供服务。现在希望找出一种合理的资源分配方案,使得同时在线人数最大化,请输出这个最大人数。

输入描述

数据组数不超过10
对于每组数据。
第一行包括两个正整数n,m(1<=n,m<=10000)
接下来m行,每行三个整数l[i],r[i],v[i]

输出描述

对于每组数据输出一个正整数,即最多容纳的用户数量

示例1

输入

5 3
1 1 1
2 4 2
2 3 2

输出

4

说明

我们可以让1号服务线服务用户1,2号服务线服务用户4,3号服务线服务用户2和3

题解

知识点:贪心,优先队列。

第一个想到的贪心是让区间的右端点从小到大排序,然后从第一个人开始遍历,看看在不在这个区间。因为这样做前面的人匹配到靠前的线路,可以让更多后面的人有机会匹配到线路。若是人的编号超过右端点了,对于这个人,这条线路就用不了了,可以丢了。

但还需要注意左端点,因为假设左端点够不到某个人,但其右端点是靠前的,那对于后面能用的人这个线路就被扔掉了,是浪费了。因此我们要对每个人,把从他开始的线路,即左端点刚好是他编号的线路,先放入优先队列按右端点从小到大排序,这样优先队列里参与匹配的线路就都是够得到当前点,且是按右端点排序好的。

所以我们一开始把线路按左端点从小到大排序,遇到一个人就把从他开始的线路加入优先队列,这样选的时候就都是够得到的线路。再把有效的线路按右端点从小到大排序,尽可能用掉右端点靠前的线路,少占用右端点靠后的线路,给后面人留机会。最后编号超过队头右端点就可以弹出队头,因为这条线路不可能用得到了。

注意,没用完的线路要放回队列。

时间复杂度 \(O(n \log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>

using namespace std;

struct node {
    int l, r, v;
}a[10007];

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, m;
    while (cin >> n >> m) {
        for (int i = 0;i < m;i++) cin >> a[i].l >> a[i].r >> a[i].v;
        sort(a, a + m, [&](node a, node b) {return a.l < b.l;});
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
        int ans = 0;
        for (int i = 1, pos = 0;i <= n;i++) {
            while (!pq.empty() && pq.top().first < i) pq.pop();
            while (pos < m && a[pos].l == i) {
                pq.push({ a[pos].r,a[pos].v });
                pos++;
            }
            if (!pq.empty()) {
                ans++;
                pair<int, int> x = pq.top();
                pq.pop();
                x.second--;
                if (x.second) pq.push(x);
            }
        }
        cout << ans << '\n';
    }
    return 0;
}
posted @ 2022-07-08 23:29  空白菌  阅读(42)  评论(0编辑  收藏  举报