当前位置:首页 > 技术 > 正文内容

算法竞赛中的数组映射、资源分配与区间最值问题解析

访客 技术 2026年10月1日 6

问题一:数组元素的最小字典序映射

题意描述:给定一个包含 $n$ 个整数的数组,要求将数组中出现的每个数字映射到一个未在该数组中出现过的正整数。映射必须满足一一对应关系(即相同的原数字必须映射到相同的新数字,不同的原数字不能映射到相同的新数字)。目标是使得映射后输出的数组字典序最小。

算法解析:为了保证字典序最小,我们需要为原数组中出现的数字分配尽可能小的未出现正整数。可以使用哈希表(或布尔数组)记录原数组中已经出现的数字。遍历原数组时,维护一个指针指向当前可用的最小正整数。对于每个元素,如果它尚未被分配映射值,则不断递增指针,直到找到一个既不在原数组中、也未被分配过的数字,然后建立映射关系。

参考实现:

#include <iostream>
#include <vector>
#include <unordered_set>
#include <unordered_map>

using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    if (!(cin >> n)) return 0;
    
    vector<int> a(n);
    unordered_set<int> present;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
        present.insert(a[i]);
    }

    unordered_map<int, int> mapping;
    int next_available = 1;
    vector<int> result(n);

    for (int i = 0; i < n; ++i) {
        if (mapping.find(a[i]) == mapping.end()) {
            // 寻找下一个未被使用且不在原数组中的正整数
            while (present.count(next_available)) {
                next_available++;
            }
            mapping[a[i]] = next_available;
            present.insert(next_available); // 标记该数字已被使用
        }
        result[i] = mapping[a[i]];
    }

    for (int i = 0; i < n; ++i) {
        cout << result[i] << (i == n - 1 ? "" : " ");
    }
    cout << "\n";
    return 0;
}

问题二:学习资源的最大化分配

题意描述:共有 $m$ 个学习资源需要分配给 $n$ 个人。每个人至少需要 $k$ 个资源才能激活学习状态。激活后,每人学习 $t$ 分钟,每分钟获得 1 点收益,但单人收益上限为 $q$(即单人收益为 $\min(x \cdot t, q)$,其中 $x$ 为分配的资源数)。目标是合理分配这 $m$ 个资源,使得所有人的总收益最大化。

算法解析:这是一个典型的数学建模与贪心分配问题。设分配给某人的资源数为 $x$,其收益函数为 $f(x) = \min(x \cdot t, q)$ (当 $x \ge k$),否则为 0。由于 $f(x)$ 在 $x \ge k$ 时单调递增,且当 $x \ge \lceil q/t \rceil$ 时达到上限 $q$,最优策略是优先让尽可能多的人达到收益上限,剩余的资源再分配给下一个人(前提是满足 $k$ 的激活条件)。在实现时,必须使用 64 位整数(long long)以防止中间计算结果溢出。

参考实现:

#include <iostream>
#include <algorithm>

using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    long long n, m, k, q, t;
    if (!(cin >> n >> m >> k >> q >> t)) return 0;

    // 计算单人达到收益上限 q 所需的最小资源数
    long long limit = max(k, (q + t - 1) / t);
    
    long long max_profit = 0;
    
    // 策略1:优先让多人达到收益上限
    long long full_count = m / limit;
    long long remainder = m % limit;
    
    long long profit1 = full_count * q;
    if (remainder >= k) {
        profit1 += min(remainder * t, q);
    }
    max_profit = max(max_profit, profit1);
    
    // 策略2:将所有资源集中分配给一个人(边界情况处理)
    if (m >= k) {
        max_profit = max(max_profit, min(m * t, q));
    }

    cout << max_profit << "\n";
    return 0;
}

问题三:特定元素集合的合法子区间统计

题意描述:给定一个长度为 $n$ 的数组,元素仅包含 -2, -1, 1, 2。要求统计有多少个连续子区间,满足区间内最大值的绝对值等于最小值的绝对值(即 $|\max| = |\min|$)。

算法解析:由于元素集合的特殊性,合法区间可以分为三类互不重叠的情况: 1. 区间内所有元素的绝对值完全相同(如全为 1,或全为 -2)。 2. 区间内同时包含 1 和 -1,且不包含 2 和 -2。 3. 区间内同时包含 2 和 -2,且不包含 1 和 -1。 我们可以采用分块与容斥原理在 $O(N)$ 时间复杂度内解决。首先遍历数组,统计所有绝对值相同的连续段贡献的子区间数。接着,将绝对值为 2 的元素视为"障碍",在由 1 和 -1 组成的连续段中,利用容斥原理(总子区间数 - 全为 1 的子区间数 - 全为 -1 的子区间数)计算同时包含 1 和 -1 的区间数。对 2 和 -2 的情况进行同理计算。

参考实现:

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>

using namespace std;

// 辅助函数:计算只包含 val1 和 val2 的连续段中,同时包含 val1 和 val2 的子区间数量
long long countMixedSubarrays(const vector<int>& a, int val1, int val2, int block1, int block2) {
    long long total = 0;
    int n = a.size();
    int i = 0;
    while (i < n) {
        if (abs(a[i]) != block1 && abs(a[i]) != block2) {
            i++;
            continue;
        }
        
        int j = i;
        long long len = 0;
        long long same1 = 0, same2 = 0;
        
        while (j < n && (abs(a[j]) == block1 || abs(a[j]) == block2)) {
            len++;
            j++;
        }
        
        long long curr_same = 0;
        for (int k = i; k < j; ++k) {
            if (a[k] == val1) {
                curr_same++;
            } else {
                same1 += curr_same * (curr_same + 1) / 2;
                curr_same = 0;
            }
        }
        same1 += curr_same * (curr_same + 1) / 2;
        
        curr_same = 0;
        for (int k = i; k < j; ++k) {
            if (a[k] == val2) {
                curr_same++;
            } else {
                same2 += curr_same * (curr_same + 1) / 2;
                curr_same = 0;
            }
        }
        same2 += curr_same * (curr_same + 1) / 2;
        
        total += len * (len + 1) / 2 - same1 - same2;
        i = j;
    }
    return total;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    if (!(cin >> n)) return 0;
    
    vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }

    long long ans = 0;

    // 1. 统计绝对值全相同的连续子区间
    int i = 0;
    while (i < n) {
        int j = i;
        while (j < n && abs(a[j]) == abs(a[i])) {
            j++;
        }
        long long len = j - i;
        ans += len * (len + 1) / 2;
        i = j;
    }

    // 2. 统计包含 1 和 -1,但不包含 2 和 -2 的子区间
    ans += countMixedSubarrays(a, 1, -1, 1, 1);

    // 3. 统计包含 2 和 -2,但不包含 1 和 -1 的子区间
    ans += countMixedSubarrays(a, 2, -2, 2, 2);

    cout << ans << "\n";
    return 0;
}
标签: C++算法竞赛

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。