题解:洛谷 P1809 过河问题

【题目来源】

洛谷:P1809 过河问题 - 洛谷

【题目描述】

有一个大晴天,Oliver 与同学们一共 \(N\) 人出游,他们走到一条河的东岸边,想要过河到西岸。而东岸边有一条小船。

船太小了,一次只能乘坐两人。每个人都有一个渡河时间 \(T\),船划到对岸的时间等于船上渡河时间较长的人所用时间。

现在已知 \(N\) 个人的渡河时间 \(T\),Oliver 想要你告诉他,他们最少要花费多少时间,才能使所有人都过河。

注意,只有船在东岸(西岸)的人才能坐上船划到对岸。

【输入】

输入文件第一行为人数 \(N\),以下有 \(N\) 行,每行一个数。

\(i+1\) 行的数为第 \(i\) 个人的渡河时间。

【输出】

输出文件仅包含一个数,表示所有人都渡过河的最少渡河时间。

【输入样例】

4
6
7
10
15

【输出样例】

42

【核心思想】

  1. 问题分析:给定 \(N\) 个人,每个人有过河时间 \(T_i\)。只有一艘船,最多载两人,船划到对岸的时间等于船上渡河时间较长的人所用时间。需要让所有人过河,使得总时间最短。这是一个经典的贪心策略问题,关键在于每次送最慢的两个人过河时的最优方案选择。

  2. 算法选择

    • 排序:将所有人按过河时间从小到大排序
    • 贪心策略:每次考虑送最慢的两个人过河,有两种方案,选择时间较短的方案
    • 方案比较
      • 方案1:最快的人带手电筒送最慢的两个人过河
        • 最快和次快的人先过河(\(t_2\)),最快的人回来(\(t_1\)
        • 最慢和次慢的人过河(\(t_{idx}\)),次快的人回来(\(t_2\)
        • 总时间:\(t_2 + t_1 + t_{idx} + t_2\)
      • 方案2:最快的人分别送最慢的两个人过河
        • 最快和最慢的人过河(\(t_{idx}\)),最快的人回来(\(t_1\)
        • 最快和次慢的人过河(\(t_{idx-1}\)),最快的人回来(\(t_1\)
        • 总时间:\(t_{idx-1} + t_1 + t_{idx} + t_1\)
  3. 关键步骤

    • 读取输入\(N\)(人数)、\(T[1..N]\)(每个人的过河时间)
    • 排序sort(t + 1, t + n + 1) 按过河时间从小到大排序
    • 贪心送最慢的两人过河(从后往前,每次 \(idx -= 2\)):
      • 计算方案1时间:p_1 = t[2] + t[1] + t[idx] + t[2]
      • 计算方案2时间:p_2 = t[idx-1] + t[1] + t[idx] + t[1]
      • 选择较短时间:ans += min(p_1, p_2)
    • 边界处理
      • 剩余2人:ans += t[2](两人一起过河)
      • 剩余3人:ans += t[1] + t[2] + t[3](最快送最慢,最快回来,最快和次快一起过河)
    • 输出结果:最少总时间 \(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \log N)\),主要是排序复杂度
    • 空间复杂度:\(O(N)\),存储过河时间
  5. 贪心策略的核心思想

    • 局部最优选择:每次送最慢的两个人过河时,选择两种方案中时间较短的方案
    • 方案比较逻辑:方案1适合最快和次快的人速度相近的情况,方案2适合最快的人速度远快于其他人的情况
    • 边界情况处理:剩余2人或3人时需要特殊处理,避免死循环
    • 贪心正确性:每次局部最优选择可以推导出全局最优解
    • 适用于资源调度、时间优化、配对问题等场景

【算法标签】

普及+ #贪心

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 100005;
int n, t[N], ans, idx;  // n: 人数, t: 过河时间, ans: 总时间, idx: 当前最慢的人
signed main()
{
    cin >> n;  // 输入人数
    for (int i = 1; i <= n; i++)  // 输入每个人的过河时间
    {
        cin >> t[i];
    }
    sort(t + 1, t + n + 1);  // 按过河时间从小到大排序
    for (idx = n; idx > 3; idx -= 2)  // 每次送最慢的两个人过河
    {
        int p_1 = t[2] + t[1] + t[idx] + t[2];  // 方案1
        int p_2 = t[idx - 1] + t[1] + t[idx] + t[1];  // 方案2
        ans += min(p_1, p_2);  // 选择时间短的方案
    }
    if (idx == 2)  // 如果还剩2个人
    {
        ans += t[2];  // 最快的两人一起过
    }
    else  // 如果还剩3个人
    {
        ans += t[1] + t[2] + t[3];  // 特殊处理
    }
    cout << ans << endl;  // 输出最少总时间
    return 0;
}

【运行结果】

4
6
7
10
15
42
posted @ 2026-06-25 09:14  团爸讲算法  阅读(15)  评论(0)    收藏  举报