题解:洛谷 P1809 过河问题
【题目来源】
【题目描述】
有一个大晴天,Oliver 与同学们一共 \(N\) 人出游,他们走到一条河的东岸边,想要过河到西岸。而东岸边有一条小船。
船太小了,一次只能乘坐两人。每个人都有一个渡河时间 \(T\),船划到对岸的时间等于船上渡河时间较长的人所用时间。
现在已知 \(N\) 个人的渡河时间 \(T\),Oliver 想要你告诉他,他们最少要花费多少时间,才能使所有人都过河。
注意,只有船在东岸(西岸)的人才能坐上船划到对岸。
【输入】
输入文件第一行为人数 \(N\),以下有 \(N\) 行,每行一个数。
第 \(i+1\) 行的数为第 \(i\) 个人的渡河时间。
【输出】
输出文件仅包含一个数,表示所有人都渡过河的最少渡河时间。
【输入样例】
4
6
7
10
15
【输出样例】
42
【核心思想】
-
问题分析:给定 \(N\) 个人,每个人有过河时间 \(T_i\)。只有一艘船,最多载两人,船划到对岸的时间等于船上渡河时间较长的人所用时间。需要让所有人过河,使得总时间最短。这是一个经典的贪心策略问题,关键在于每次送最慢的两个人过河时的最优方案选择。
-
算法选择:
- 排序:将所有人按过河时间从小到大排序
- 贪心策略:每次考虑送最慢的两个人过河,有两种方案,选择时间较短的方案
- 方案比较:
- 方案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\)
- 方案1:最快的人带手电筒送最慢的两个人过河
-
关键步骤:
- 读取输入:\(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)
- 计算方案1时间:
- 边界处理:
- 剩余2人:
ans += t[2](两人一起过河) - 剩余3人:
ans += t[1] + t[2] + t[3](最快送最慢,最快回来,最快和次快一起过河)
- 剩余2人:
- 输出结果:最少总时间 \(ans\)
-
时间/空间复杂度:
- 时间复杂度:\(O(N \log N)\),主要是排序复杂度
- 空间复杂度:\(O(N)\),存储过河时间
-
贪心策略的核心思想:
- 局部最优选择:每次送最慢的两个人过河时,选择两种方案中时间较短的方案
- 方案比较逻辑:方案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
浙公网安备 33010602011771号