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

课桌分配优化问题

访客 技术 2026年5月24日 4

本题的核心在于利用单调性优化分配策略,从而降低时间复杂度。

问题描述

给定若干课桌和学生,每张课桌有一个范围 [l, r] 表示可以容纳的学生高度范围。需要将学生分配到课桌上,使得总不适值最小化。

解题思路

首先,我们可以通过排除那些完全无用的课桌(即被其他课桌完全覆盖的范围)来减少计算量。

接着,假设我们已经选定了某些课桌,并按照左端点排序。那么对于每个学生组,应该按照高度从低到高依次安排到这些课桌上。

我们可以重新定义问题:不再考虑分组,而是直接将同一张课桌上的学生视为一组,这样共有 2m 组学生。然后对这 2m 组学生进行分治处理,利用课桌的单调性递归求解左右子问题。

算法实现

以下是两种实现方式:

非精细实现


#include 
using namespace std;

typedef long long LL;
const int MAXN = 200005;
int m, n, k;
bool tag[MAXN];
struct Table {
    int l, r;
} tbs[MAXN];

LL calc(LL s_l, LL s_r, LL tbl) {
    LL res = 0;
    if (s_l < tbl.l) res += tbl.l - s_l;
    if (s_l > tbl.r) res += s_l - tbl.r;
    if (s_r < tbl.l) res += tbl.l - s_r;
    if (s_r > tbl.r) res += s_r - tbl.r;
    return res;
}

LL divide(int st, int ed, int tbl_s, int tbl_e) {
    if (st > ed) return 0;
    LL ret = 0;
    if (tbl_s == tbl_e) {
        for (int i = 1; i <= m; ++i)
            for (int j = st; j <= ed; ++j)
                ret += calc(tbs[j].l, tbs[j].r, tbs[tbl_s]);
        return ret;
    }
    int mid = (st + ed) / 2, best = tbl_s;
    LL min_cost = LLONG_MAX;
    for (int i = tbl_s; i <= tbl_e; ++i) {
        LL tmp = 0;
        for (int j = 1; j <= m; ++j)
            tmp += calc(tbs[mid].l, tbs[mid].r, tbs[i]);
        if (tmp < min_cost) {
            min_cost = tmp;
            best = i;
        }
    }
    return min_cost + divide(st, mid - 1, tbl_s, best) + divide(mid + 1, ed, best, tbl_e);
}

int main() {
    cin >> m >> n >> k;
    for (int i = 1; i <= k; ++i) cin >> tbs[i].l >> tbs[i].r;
    sort(tbs + 1, tbs + k + 1, [&](Table a, Table b) -> bool {
        if (a.l != b.l) return a.l < b.l;
        return a.r > b.r;
    });
    // 去重
    int cnt = 0;
    for (int i = 1; i <= k; ++i) {
        if (cnt && tbs[cnt].l <= tbs[i].l && tbs[cnt].r >= tbs[i].r) continue;
        tbs[++cnt] = tbs[i];
    }
    k = cnt;
    cout << divide(1, n, 1, k) << endl;
    return 0;
}

精细实现


#include 
using namespace std;

typedef long long LL;
const int MAXN = 200005;
int m, n, k;
bool tag[MAXN];
struct Table {
    int l, r;
} tbs[MAXN];

LL get_cost(int h, Table &t) {
    if (h < t.l) return t.l - h;
    if (h > t.r) return h - t.r;
    return 0;
}

LL prefix_sum[2 * MAXN];
LL query(int l, int r) {
    if (l > r) return 0;
    return prefix_sum[r] - (l ? prefix_sum[l - 1] : 0);
}

LL solve(int st, int ed, int tbl_st, int tbl_ed) {
    if (st > ed) return 0;
    int mid = (st + ed) / 2, opt = tbl_st;
    LL min_val = LLONG_MAX;
    // 计算前缀和
    for (int i = 0; i < 2 * m; ++i) prefix_sum[i] = (i ? prefix_sum[i - 1] : 0) + sorted_students[mid][i];
    for (int i = tbl_st; i <= tbl_ed; ++i) {
        int less = 0, more = 0;
        while (less < 2 * m && sorted_students[mid][less] < tbs[i].l) ++less;
        while (more < 2 * m && sorted_students[mid][more] <= tbs[i].r) ++more;
        LL cost = 1LL * tbs[i].l * less - query(0, less - 1) + query(more, 2 * m - 1) - 1LL * (2 * m - more) * tbs[i].r;
        if (cost < min_val) {
            min_val = cost;
            opt = i;
        }
    }
    return min_val + solve(st, mid - 1, tbl_st, opt) + solve(mid + 1, ed, opt, tbl_ed);
}

int main() {
    cin >> m >> n >> k;
    for (int i = 1; i <= k; ++i) cin >> tbs[i].l >> tbs[i].r;
    sort(tbs + 1, tbs + k + 1, [&](Table a, Table b) -> bool {
        if (a.l != b.l) return a.l < b.l;
        return a.r > b.r;
    });
    int unique = 0;
    for (int i = 1; i <= k; ++i) {
        if (unique && tbs[unique].l <= tbs[i].l && tbs[unique].r >= tbs[i].r) continue;
        tbs[++unique] = tbs[i];
    }
    k = unique;
    vector sorted_students(n, vector<int>());
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < 2 * n; ++j) cin >> heights[j];
        sort(heights, heights + 2 * n);
        for (int j = 0; j < 2 * n; j += 2) {
            sorted_students[j / 2].push_back(heights[j]);
            sorted_students[j / 2].push_back(heights[j + 1]);
        }
    }
    for (int i = 0; i < n; ++i) sort(sorted_students[i].begin(), sorted_students[i].end());
    cout << solve(0, n - 1, 1, k) << endl;
    return 0;
}

时间复杂度

该算法的时间复杂度为 O((k + nm) log n),其中 k 是课桌数量,n 是学生组数,m 是每组学生的数量。

相关文章

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...

发表评论

访客

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