NC207781 迁徙过程中的河流

题目链接

题目

题目描述

牛市的幸存的先民在流星雨之后就忍痛离开了这片土地,选择迁徙,在迁徙的途中,他们需要渡过一条河。因为牛市的树木在流星雨中被严重破坏,所以他们只造出了一艘小船,船太小了,一次只能乘坐两人。
牛市的先民们每个人划船的速度都不尽相同,所以每个人都有一个渡河时间T,为了保证船的平衡,当穿上有两个人的时候,需要他们按照慢的那个人的速度划船,也就是说船到达对岸的时间等于船上渡河时间长的那个人的时间。
现在已知N个人的渡河时间T,请问最少要花费多少时间,才能使所有人都过河。

输入描述

输入文件第一行为先民的人数 \(N\) \((N\leq 100000)\) ,以下有N行,每行一个整数为每个人的渡河时间。

输出描述

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

示例1

输入

4
5
7
11
16

输出

42

说明

首先1,2先到河对岸花费7,然后1回来花费5,3,4到河对岸花费16,2回来花费7,1,2再到河对岸花费7

题解

知识点:线性dp,贪心。

给出一个贪心结论,因为时间短的和时间长的一起无法发挥时间短的收益,因此要么用时间最短的人作为运输媒介来回跑(这因为最短的人作媒介一定使得来回运输一个人的成本最低),要么两个时长相近的人一起。显然,先从小到大排序,方便处理。

\(dp[i]\) 表示运到第 \(i\) 个人的最短时间。先让最短时间的两人过去作为运输媒介,\(dp[1] = t[1],dp[2] = t[2]\) 。第一种方法:先用 \(t[1]\)\(1\) 号回去,用 \(t[i]\)\(i\) 号过去,共 \(t[1] + t[i]\) ;第二种方法:先用 \(t[1]\)\(1\) 回去,再用 \(t[i]\)\(i-1\)\(i\) 过去,再用 \(t[2]\)\(2\) 回去,用 \(t[2]\)\(1\)\(2\) 过去,共 \(t[1] + t[i] + 2t[2]\) 。于是有转移方程:

\[dp[i] = \min (dp[i-1]+t[1]+t[i],dp[i-2]+t[1]+t[i]+2t[2]) \]

时间复杂度 \(O(n\log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>

using namespace std;

int t[100007], dp[100007];///表示运了i个人

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1;i <= n;i++) cin >> t[i];
    sort(t + 1, t + n + 1);///从小到大运人
    dp[1] = t[1];
    dp[2] = t[2];
    for (int i = 3;i <= n;i++) dp[i] = min(dp[i - 1] + t[1] + t[i], dp[i - 2] + t[1] + t[i] + 2 * t[2]);
    ///先让最小的两人过去,用这两个人运。因为能坐两个人,所以可以考虑两种情况
    ///1回来接i过去;1回来i-1和i过去,2回来接1回去
    cout << dp[n] << '\n';
    return 0;
}
posted @ 2022-08-10 23:20  空白菌  阅读(33)  评论(0编辑  收藏  举报