牛客刷题-Day14

牛客刷题

今日刷题:\(1006-1010\)

1006 [JSOI2010]缓存交换

题目描述

在计算机中,\(CPU\) 只能和高速缓存 \(Cache\) 直接交换数据。当所需的内存单元不在 \(Cache\) 中时,则需要从主存里把数据调入 \(Cache\)。此时,如果 \(Cache\) 容量已满,则必须先从中删除一个。
例如,当前 \(Cache\) 容量为 \(3\),且已经有编号为 \(10\)\(20\) 的主存单元。此时,\(CPU\) 访问编号为 \(10\) 的主存单元,\(Cache\) 命中。接着,\(CPU\) 访问编号为 \(21\) 的主存单元,那么只需将该主存单元移入 \(Cache\) 中,造成一次缺失(\(Cache\, Miss\))。接着,\(CPU\) 访问编号为 \(31\) 的主存单元,则必须从 \(Cache\) 中换出一块,才能将编号为 \(31\) 的主存单元移入 \(Cache\),假设我们移出了编号为 \(10\) 的主存单元。接着,\(CPU\) 再次访问编号为 \(10\) 的主存单元,则又引起了一次缺失。
我们看到,如果在上一次删除时,删除其他的单元,则可以避免本次访问的缺失。 在现代计算机中,往往采用 \(LRU\)(最近最少使用)的算法来进行 \(Cache\) 调度——可是,从上一个例子就能看出,这并不是最优的算法。
对于一个固定容量的空 \(Cache\) 和连续的若干主存访问请求,聪聪想知道如何在每次Cache缺失时换出正确的主存单元,以达到最少的 \(Cache\) 缺失次数。

输入描述

输入文件第一行包含两个整数 \(N\)\(M(1 ≤ M ≤ N ≤ 100,000)\),分别代表了主存访问的次数和 \(Cache\) 的容量。
第二行包含了 \(N\) 个空格分开的正整数,按访问请求先后顺序给出了每个主存块的编号(不超过 \(1,000,000,000\))。

输出描述

输出一行,为 \(Cache\) 缺失次数的最小值。

示例

输入

6 2
1 2 3 1 2 3

输出

4

解题思路

按照顺序处理,记录每个主存单元下一次出现的下标,以下标入大根堆。

C++ 代码

#include <bits/stdc++.h>
using namespace std;
const int N = 100010;

int n, m;
int a[N], nxt[N];
map<int, int> tmp, cache;
priority_queue<int> q;

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for (int i = n; i; i--) { // 记录主存单元的下一次访问
        if (!tmp[a[i]]) {
            nxt[i] = 0x3f3f3f3f;
        } else {
            nxt[i] = tmp[a[i]];
        }
        tmp[a[i]] = i;
    }
    // for (int i = 1; i <= n; i++)
    //     printf("%d ", nxt[i]);
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        if (!cache[i]) {
            cnt++;
            if (cnt > m) { // 缓存满了 最远的先删除
                cache[q.top()] = 0;
                q.pop();
            }
        }
        cache[nxt[i]] = 1;
        q.push(nxt[i]);
    }
    printf("%d\n", cnt);
    return 0;
}

1009 Operating System

题目描述

在学习 \(Operating\, System\) 的过程中,\(Glory\) 遇到了这样一个问题,现在有一个大小为可以容纳 \(N\) 个页面的内存,硬盘内的内容被分成 \(M\) 个页面,用 \(1~M\) 来标识,一开始内存里没有任何页面,接下来用户会请求 \(Q\) 个页面,你需要设计一个置换算法,使得缺页发生的次数最少。缺页是指用户请求某个编号的页面,但这个页面没有在内存中的情况。发生缺页之后,你必须要把硬盘内对应的页面调入内存中,如果内存已满,你需要置换掉当前内存中的某个页面。

输入描述

多组数据,请处理到输入结束。
每组数据,第一行为三个整数 \(N\)\(M\)\(Q\)\(0 < N,M,Q <= 50000\)
接下来一行 \(Q\) 个数,表示用户请求的页面编号。

输出描述

对于每组数据,输出一个数,表示最少的缺页次数。

示例

输入

2 3 5
3 1 2 1 2
3 4 5 
3 2 1 4 3

输出

3
4

解题思路

思路类似 \(1006\)

C++ 代码

#include <bits/stdc++.h>
using namespace std;
const int N = 50010;

int n, m, q;
int page[N], nxt[N];

void solve() {
    for (int i = 1; i <= q; i++)
        scanf("%d", &page[i]);
    if (n >= m) {
        set<int> s;
        for (int i = 1; i <= q; i++)
            s.insert(page[i]);
        printf("%d\n", s.size());
        return;
    }
    priority_queue<int> que;
    map<int, int> cache, tmp;
    for (int i = q; i; i--) {
        if (!tmp[page[i]]) {
            nxt[i] = 0x3f3f3f3f;
        } else {
            nxt[i] = tmp[page[i]];
        }
        tmp[page[i]] = i;
    }
    int cnt = 0;
    for (int i = 1; i <= q; i++) {
        if (!cache[i]) {
            cnt++;
            if (cnt > n) {
                cache[que.top()] = 0;
                que.pop();
            }
        }
        cache[nxt[i]] = 1;
        que.push(nxt[i]);
    }
    printf("%d\n", cnt);
}

int main() {
    while (~scanf("%d%d%d", &n, &m, &q))
        solve();
    return 0;
}

1010 网络优化

题目描述

《梦三国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]\)

输出描述

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

示例

输入

5 3
1 1 1
2 4 2
2 3 2

输出

4

说明:可以让 \(1\) 号服务线服务用户 \(1\)\(2\) 号服务线服务用户 \(4\)\(3\) 号服务线服务用户 \(2\)\(3\)

解题思路

按照左边界 \(l\) 进行排序,先处理在左边界的节点,然后按照右边界自小到大排序,用完一个区间,再分配下一个区间。

C++ 代码

#include <bits/stdc++.h>
using namespace std;
const int N = 10010;
typedef pair<int, int> PII;

int n, m;
struct Node {
    int l, r, v;
} a[N];

bool cmp(Node a, Node b) {
    return a.l < b.l;
}

int main() {
    while (~scanf("%d%d", &n, &m)) {
        for (int i = 1; i <= m; i++)
            scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].v);
        sort(a + 1, a + m + 1, cmp);
        priority_queue<PII, vector<PII>, greater<PII>> q;
        int cnt = 0;
        for (int i = 1, j = 1; i <= n; i++) {
            while (!q.empty() && q.top().first < i)
                q.pop();
            while (j <= m && a[j].l == i) {
                q.push({a[j].r, a[j].v});
                j++;
            }
            if (!q.empty()) {
                cnt++;
                auto p = q.top();
                q.pop();
                p.second--;
                if (p.second)
                    q.push(p);
            }
        }
        printf("%d\n", cnt);
    }
    return 0;
}

1007 背包

题目描述

\(Applese\)\(1\) 个容量为 \(v\) 的背包,有 \(n\) 个物品,每一个物品有一个价值 \(a_i\),以及一个大小 \(b_i\)
然后他对此提出了自己的疑问,如果我不要装的物品装的价值最大,只是一定需要装 \(m\) 个物品,要使得求出来的物品价值的中位数最大。
\(Applese\) 觉得这个题依然太菜,于是他把这个问题丢给了你。
当物品数量为偶数时,中位数即中间两个物品的价值的平均值。

输入描述

第一行三个数 \(v, n, m\),分别代表背包容量,物品数量以及需要取出的物品数量。
接下来 \(n\) 行,每行两个数 \(a_i\)\(b_i\),分别代表物品价值以及大小。
\(n ≤ 1e^5, 1 ≤ m ≤ n, a_i ≤ 1e^9, v ≤ 1e^9, b_i ≤ v\)

输出描述

仅一行,输出一个整数 ,代表最大的中位数,如果物品数量是偶数输出两个中位数的平均值向下取整后的结果

示例

输入

20 5 3
3 5
5 6
8 7
10 6
15 10

输出

8

解题思路

参考:NC17315 背包
贪心、优先队列、二分。
这里需要求解选择 \(m\) 个物品且体积和不超过 \(v\) 并且要求选择的物品价值中位数尽量大。
预处理:按照价值递增进行排序;计算前后缀和数组,这里的前后缀和要尽量小,以保证中位数对应的物品可以被选择。即对于 \(pre_i\),如果 \(i\ge \lfloor{\frac{m}{2}}\rfloor\),则取 \(\lfloor{\frac{m}{2}}\rfloor\) 个体积最小的;对于 \(suf_i\),类似 \(pre_i\),只是倒序的处理一遍。

如果 \(m\) 是奇数,则是维护 \(\lfloor{\frac{m}{2}}\rfloor\),否则两个数组维护的是 \(\frac{m}{2}-1\)

因为中位数的取值分为奇偶数两种情况,分开讨论:

  • 奇数:直接枚举中位数 \(a_i.value\),则此时的体积为 \(pre_{i-1}+a_i.size+suf_{i+1}\),如果不超过 \(v\),则可以取当前值。
  • 偶数:因为涉及两个数,枚举左侧中位数,寻找右侧中位数。可以发现,\(suf\) 数组随着 \(i\) 的增大是一个递增的数组,\(i\) 的右边界保证至少选择 \(\frac{m}{2}-1\) 个数。那么就可以在保证体积和不超过 \(v\) 的情况下,右侧中位数尽量往右边去取,可以使用二分。

C++ 代码

#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
typedef long long LL;

LL v, n, m;
struct Node {
    LL value, size;
} a[N];
LL pre[N], suf[N]; // suf 是一个随 i 递增的数组

bool cmp(Node a, Node b) {
    return a.value < b.value;
}

int main() {
    scanf("%lld%lld%lld", &v, &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%lld%lld", &a[i].value, &a[i].size);
    sort(a + 1, a + n + 1, cmp);
    priority_queue<LL> q; // 大根堆
    for (int i = 1; i <= n; i++) {
        pre[i] = pre[i - 1] + a[i].size;
        q.push(a[i].size);
        if (q.size() > m / 2 - !(m & 1)) {
            pre[i] -= q.top();
            q.pop();
        }
    }
    while (!q.empty())
        q.pop();
    for (int i = n; i; i--) {
        suf[i] = suf[i + 1] + a[i].size;
        q.push(a[i].size);
        if (q.size() > m / 2 - !(m & 1)) {
            suf[i] -= q.top();
            q.pop();
        }
    }
    LL ans = 0;
    if (m & 1) {
        for (int i = m / 2 + 1; i <= n - m / 2; i++)
            if (pre[i - 1] + a[i].size + suf[i + 1] <= v)
                ans = max(ans, a[i].value);
    } else {
        for (int i = m / 2; i <= n - m / 2; i++) {
            LL t = pre[i - 1] + a[i].size;
            int l = i + 1, r = n - m / 2 + 1;
            while (l < r) {
                int mid = l + r + 1 >> 1;
                if (t + a[mid].size + suf[mid + 1] <= v)
                    l = mid;
                else
                    r = mid - 1;
            }
            if (t + a[r].size + suf[r + 1] <= v) // l = r = n - m / 2 + 1 的特判
                ans = max(ans, a[i].value + a[r].value >> 1);
        }
    }
    printf("%lld\n", ans);
    return 0;
}
posted @ 2025-10-17 14:00  Cocoicobird  阅读(5)  评论(0)    收藏  举报