n 人两两乘船过河的最少总时间:排序后贪心,每次比较「最快两人护送最慢两人」与「最快者单送最慢者」两种收尾方案取优,O(n log n)。
OJ: roj
题目 ID: 5003
难度:普及+/提高-
标签:贪心排序
创建: 2026-10-02 15:55
更新: 2026-10-10 11:30
形式化题目
正解
思路
1. 贪心收尾
按时间升序排列,最快两人记
- 护送:
过河( )→ 划回( )→ 过河( )→ 划回( ),合计 ,一次送走两人; - 单送:
过河( )→ 划回( ),合计 ,一次送走一人;走两次单送 共 。
比较得:
2. 正确性直觉
最优解里最快的
代码
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-10 11:30
* update_at: 2026-10-10 11:30
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){ios::sync_with_stdio(false);cin.tie(nullptr); int n; if(!(cin>>n)) return 0; vector<ll>a(n); for(int i=0;i<n;i++) cin>>a[i]; sort(a.begin(),a.end()); ll total=0; int l=0,r=n-1; while(r-l>2){ll aa=a[l],bb=a[l+1],cc=a[r-1],dd=a[r]; if(2*bb<aa+cc){total+=aa+2*bb+dd; r-=2;} else {total+=aa+dd; r--;}} total += (r-l==2 ? a[l]+a[l+1]+a[r] : a[r]); cout<<total<<'\n'; return 0;}python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-02 15:54
# update_at: 2026-10-02 15:54
import sys
def min_cross_time(people: list[int]) -> int:
"""升序数组从最慢的一端贪心送人:返回全部过河的最少总耗时。"""
total = 0
l, r = 0, len(people) - 1 # 还没过河的人是 people[l..r]
while r - l > 2: # 人数 > 3 才需要在两种送法间选择
a, b = people[l], people[l + 1] # 最快的两人
c, d = people[r - 1], people[r] # 最慢的两人
# 护送两人:a,b 过河(b)、a 划回(a)、c,d 过河(d)、b 划回(b)
# 单送一人:a,d 过河(d)、a 划回(a);分两次单送走 c,d 合计 2a+c+d
# 护送比连续单送便宜(2b < a+c)时才一次送走最慢两人
if 2 * b < a + c:
total += a + 2 * b + d
r -= 2
else:
total += a + d
r -= 1
# 剩 3 人:a 带最慢的过河后划回、再带次慢的过去(a+b+c);剩 1、2 人直接上船
total += sum(people[l:r + 1]) if r - l == 2 else people[r]
return total
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n = next(data)
people = sorted(next(data) for _ in range(n))
print(min_cross_time(people))
if __name__ == "__main__":
solve()复杂度
- 时间复杂度:
(排序)。 - 空间复杂度:
。
总结
- 「过河双人船」是贪心经典:两种收尾方案比大小即得最优。
- 记住比较式
(护送优于连续单送的条件)。