题目描述

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

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

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

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

输入格式

输入文件第一行为人数 NN,以下有 NN 行,每行一个数。

i+1i+1 行的数为第 ii 个人的渡河时间。

输出格式

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

样例 #1

样例输入 #1

4
6
7
10
15

样例输出 #1

42

样例输入 #2

4
1
2
5
10

样例输出 #2

17

提示

数据范围

对于 40%40\% 的数据满足 N8N\le8

对于 100%100\% 的数据满足 N100000N\le100000

题目分析

这道题目很明显不能用贪心算法(虽然每一步都找最优解,但可能结果不是最优解),动态规划才是正解

样例1解释

  • 初始:东岸 {1,2,3,4}\{1,2,3,4\},西岸 {}\{\}
  • 第一次:东岸 {3,4}\{3,4\},西岸 {1,2}\{1,2\},时间 77
  • 第二次:东岸 {1,3,4}\{1,3,4\},西岸 {2}\{2\},时间 66
  • 第三次:东岸 {1}\{1\},西岸 {2,3,4}\{2,3,4\},时间 1515
  • 第四次:东岸 {1,2}\{1,2\},西岸 {3,4}\{3,4\} 时间 77
  • 第五次:东岸 {}\{\},西岸 {1,2,3,4}\{1,2,3,4\} 时间 77

所以总时间为 7+6+15+7+7=427+6+15+7+7=42,没有比这个更优的方案。

样例2解释

  • 初始:东岸 {1,2,3,4}\{1,2,3,4\},西岸 {}\{\}
  • 第一次:东岸 {3,4}\{3,4\},西岸 {1,2}\{1,2\},时间 22
  • 第二次:东岸 {1,3,4}\{1,3,4\},西岸 {2}\{2\},时间 11
  • 第三次:东岸 {1}\{1\},西岸 {2,3,4}\{2,3,4\},时间 1010
  • 第四次:东岸 {1,2}\{1,2\},西岸 {3,4}\{3,4\},时间 22
  • 第五次:东岸 {}\{\},西岸 {1,2,3,4}\{1,2,3,4\},时间 22

所以总时间为 2+1+10+2+2=172+1+10+2+2=17,没有比这个更优的方案。

大体思路

先把过河时间从小到大排序,然后过河。

过河分为两种情况:

  • AA岸只剩一个人,让BB岸最小的来接他
  • AA岸剩下两个人,让最小的到BB岸送船,然后原来BB岸的那两个人(即最大的两个人)到AA岸,让次小的到BB岸,最后一起过河

上面看不懂没关系 简单来说就是:

  • 让最小的人(t[1]t[1])来回送其他人(t[i]t[i]):dp[i]=dp[i1]+t[1]+t[i]dp[i]=dp[i-1]+t[1]+t[i]
  • t[1]t[1]带着t[2]t[2]过去,然后t[2]t[2]回来,再让t[i]t[i]t[i+1]t[i+1]过去,最后让t[1]t[1]去接t[2]t[2]过去:dp[i]=dp[i2]+t[1]+t[2]2+t[i]dp[i]=dp[i-2]+t[1]+t[2]*2+t[i]

从这两种情况中选出用时最短的,列出状态转移方程

状态转移方程

肯定先定义数组啦!

int dp[100005],t[100005];//t存每个人的过河时间

前两个人所花的时间是定值,所以先存到dpdp里面去

dp[1]=t[1];
dp[2]=t[2];

然后最最最核心的我不想写,自己去推

咳咳……

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

最后……没有最后贴上代码

#include<bits/stdc++.h>
using namespace std;

int t[100005],dp[100005];
int main(void){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>t[i];
	sort(t+1,t+1+n);
	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]+2*t[2]+t[i]);
	cout<<dp[n]<<endl;
	return 0;
}