1. 贪心算法与过河问题概述
贪心算法(Greedy Algorithm)是一种在每一步选择中都采取当前状态下最优决策的算法策略。它不像动态规划那样考虑全局最优解,而是通过局部最优的选择,希望最终达到全局最优。这种算法思想简单直接,在许多实际问题中都有出色表现。
过河问题(River Crossing Problem)是一类经典的算法题目,其基本形式是:一组人需要在特定限制条件下过河,通常涉及时间成本、运输工具限制等约束条件。洛谷P1809题目就是这类问题的典型代表,要求我们找到最优的过河方案。
提示:贪心算法虽然简单高效,但并非所有问题都适用。只有当问题具有"贪心选择性质"(即局部最优解能导致全局最优解)和"最优子结构"时,贪心算法才能给出正确解。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 洛谷P1809题目解析
2.1 题目描述与理解
P1809题目描述如下:有N个人要过河,他们过河的时间分别为t1,t2,...,tn。只有一条船,船最多能载两人。当两人一起划船时,速度由较慢者决定。求所有人过河的最短总时间。
这个问题的核心在于:
- 运输工具的容量限制(最多两人)
- 时间计算的特殊规则(取两人中的较慢者)
- 需要往返运输(船需要返回才能继续运输)
2.2 问题建模与输入输出
我们可以将问题建模为:
- 输入:一个整数N表示人数,接下来N个整数表示每个人的过河时间
- 输出:一个整数表示所有人过河的最短总时间
例如:
输入:
4
1 2 5 10
输出:
17
3. 贪心策略设计与分析
3.1 基本贪心思路
对于这类过河问题,常见的贪心策略有两种:
- 最快者运输法:让过河最快的人多次往返运输其他人
- 配对运输法:让最慢的两人一起过河,减少他们的单独运输时间
在实际应用中,我们需要结合这两种策略才能得到最优解。
3.2 具体算法步骤
- 首先将所有人按过河时间从小到大排序
- 考虑剩余人数:
- 如果剩下3人:让最快的两人先过河,最快的返回,然后最慢的两人过河,次快的返回
- 如果剩下2人:两人一起过河
- 如果剩下1人:直接过河
- 否则:
a. 方案1:最快的两人先过河,最快的返回,最慢的两人过河,次快的返回
b. 方案2:最快的和最慢的一起过河,最快的返回
c. 选择两种方案中耗时较少的
3.3 算法正确性证明
这个贪心策略的正确性基于以下观察:
- 最慢的两个人一起过河比分开过河更节省时间
- 让最快的两人多次往返可以最小化"空船返回"的时间损失
- 对于剩余3人的特殊情况需要单独处理
4. 代码实现与优化
4.1 C++实现示例
cpp复制#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int n;
cin >> n;
int t[n];
for(int i=0; i<n; i++) cin >> t[i];
sort(t, t+n);
int res = 0;
while(n > 3) {
// 比较两种策略的耗时
int plan1 = t[0] + 2*t[1] + t[n-1];
int plan2 = 2*t[0] + t[n-1] + t[n-2];
res += min(plan1, plan2);
n -= 2;
}
// 处理剩余人数
if(n == 3) {
res += t[0] + t[1] + t[2];
} else if(n == 2) {
res += t[1];
} else {
res += t[0];
}
cout << res << endl;
return 0;
}
4.2 时间复杂度分析
算法的主要时间消耗在排序上,使用快速排序时间复杂度为O(nlogn)。后面的贪心策略处理是线性时间O(n),因此总时间复杂度为O(nlogn)。
4.3 边界条件处理
需要特别注意的特殊情况包括:
- 只有1个人时:直接输出他的过河时间
- 只有2个人时:输出较慢者的时间
- 当n很大时:确保排序算法效率足够高
5. 算法优化与变种
5.1 空间优化
如果内存受限,可以在排序后只保留必要的几个值(最快两人和最慢两人),而不是存储整个数组。
5.2 不同约束条件的变种
过河问题有多种变种,每种可能需要不同的贪心策略:
- 船容量变化(如最多3人)
- 时间计算规则变化(如取平均值)
- 不同方向的运输需求(如需要运送物品)
5.3 与其他算法的对比
虽然贪心算法在这里表现良好,但也可以考虑其他方法:
- 动态规划:状态表示更复杂但可能找到更优解
- 回溯法:在小规模问题中可以穷举所有可能
- 分支限界法:在特定约束下可能更高效
6. 实战经验与常见错误
6.1 调试技巧
在实现这类算法时,建议:
- 先手动计算小规模案例(如4人)验证算法
- 打印中间结果检查策略选择是否正确
- 特别注意边界条件的处理
6.2 常见错误
初学者常犯的错误包括:
- 忘记排序或排序方向错误
- 没有正确处理剩余3人的特殊情况
- 在计算总时间时漏算返回时间
- 没有比较两种策略而固定使用其中一种
6.3 性能优化建议
对于大规模数据:
- 使用更快的排序算法(如内建的sort)
- 避免不必要的变量和计算
- 考虑输入输出的效率(如使用scanf/printf代替cin/cout)
7. 洛谷平台提交注意事项
在洛谷提交代码时需要注意:
- 确保使用正确的语言选项(C++11/C++14等)
- 处理输入输出格式必须完全匹配题目要求
- 注意时间限制,确保算法效率足够
- 可以查看其他用户的题解学习不同思路
注意:洛谷的测试用例可能包含极端情况,如n=1或n=1e5,确保你的代码能处理所有可能情况。
