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

基于二分法和动态规划求解一维人群相遇问题

访客 技术 2026年6月1日 1

AtCoder 原题大意:
给定 n 个人在数轴上的坐标 a[i](均为偶数),每个人的最大移动速度为 1。要求每对相邻的人(即第 i 个和第 i+1 个)之间都发生过相遇,求达成该条件所需的最短时间。

数据范围:1 ≤ n ≤ 2×10^50 ≤ a[i] ≤ 10^9,坐标严格递增。

解法概述

本解法的时间复杂度为 O(n log n)

核心思路:对答案时间 t 进行二分查找,并使用动态规划(DP)来验证在给定时间内是否能满足所有相遇条件。

人的两种可能行为

根据最优策略,每个人只有两种行动模式:

  • 模式 A:先向右移动与右侧邻居相遇,然后折返向左移动。
  • 模式 B:先向左移动与左侧邻居相遇,然后折返向右移动。

动态规划设计

定义二维数组 dp[i][0]dp[i][1],两值均表示第 i 个人在时间结束时可到达的最右位置坐标(相对于其起始位置)。区别在于:

  • dp[i][0]:此人采用模式 A(先向右)时,能向右移动的最大额外距离。
  • dp[i][1]:此人采用模式 B(先向左)时,能向右移动的最大额外距离。

对于第一个人,由于没有左侧邻居,两种模式初始值均为 t(全速向右移动获得的最大距离)。

转移策略推导

考虑第 i-1 个人(前驱)与第 i 个人(当前)的行动模式组合。为了可读性,下文用 "前者" 指代第 i-1 人,"后者" 指代第 i 人。

情况 1:前者模式 A + 后者模式 B

前者会先向右移动到达某个点,为保证相遇,后者必须在同一时间点到达该点。因此,前者的可达右边界 a[i-1] + dp[i-1][0] 必须不小于后者能向左到达的最左边界 a[i] - dp[i-1][0]

if (a[i-1] + dp[i-1][0] >= a[i] - dp[i-1][0]) {
    // 条件成立
}

相遇后,后者会折返向右,其可用于向右移动的总时间为 t - (a[i] - a[i-1]),因此转移如下:

dp[i][1] = max(dp[i][1], t - (a[i] - a[i-1]));

情况 2:前者模式 A + 后者模式 A

两人均先向右,初始间距保持不变。一旦前者折返向左,后者将无法追上,因此需要限制在前者折返前能到达的最大位置。

dp[i][0] = max(dp[i][0], dp[i-1][0] - (a[i] - a[i-1]) / 2);

情况 3:前者模式 B + 后者模式 A

前者在 t 秒末的位置为 a[i-1] + dp[i-1][1]。为了保证相遇,后者必须能在 t 秒内到达该点,即 a[i-1] + dp[i-1][1] >= a[i] - t

if (a[i-1] + dp[i-1][1] >= a[i] - t) {
    // 条件成立
}

相遇后,后者向右移动可用的总时间为 t - (a[i] - (a[i-1] + dp[i-1][1])),由于需要折返,实际向右移动净距离需除以 2:

dp[i][0] = max(dp[i][0], (t - a[i] + a[i-1] + dp[i-1][1]) / 2);

情况 4:前者模式 B + 后者模式 B

两人均先向左移动,相遇后立即一起向右移动。后者的最大向右距离取决于前者的最终位置减去当前间距。

dp[i][1] = max(dp[i][1], dp[i-1][1] + a[i-1] - a[i]);

验证函数实现

二分时通过 check(t) 函数判断 t 时间是否可行。若存在任意一人无法进行合法转移,则 t 不可行。

int dp[MAXN][2];
// dp[i][0]: 先向右的最大右移; dp[i][1]: 先向左的最大右移

void update(int &x, int y) { x = max(x, y); }

bool check(int t) {
    for (int i = 2; i <= n; ++i)
        dp[i][0] = dp[i][1] = -INF;
    dp[1][0] = dp[1][1] = t;

    for (int i = 2; i <= n; ++i) {
        bool ok = false;

        // 情况1+2:前者先右
        if (dp[i-1][0] >= (a[i] - a[i-1]) / 2) {
            update(dp[i][1], t - (a[i] - a[i-1]));
            update(dp[i][0], dp[i-1][0] - (a[i] - a[i-1]) / 2);
            ok = true;
        }

        // 情况3+4:前者先左
        if (a[i-1] + dp[i-1][1] >= a[i] - t) {
            update(dp[i][0], (t - a[i] + a[i-1] + dp[i-1][1]) / 2);
            update(dp[i][1], dp[i-1][1] + a[i-1] - a[i]);
            ok = true;
        }

        if (!ok) return false;
    }
    return true;
}

主二分流程

二分下限 l = 0,上限 r = (a[n] - a[1]) / 2,并在过程中更新最小可行时间 ans

int main() {
    // 输入处理省略
    int ans = (a[n] - a[1]) / 2;
    int l = 0, r = (a[n] - a[1]) / 2;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) {
            ans = mid;
            r = mid - 1;
        } else {
            l = mid + 1;
        }
    }
    // 输出 ans
    return 0;
}
标签: AtCoder

相关文章

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

发表评论

访客

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