【题目链接】
ybt 1232:Crossing River
OpenJudge NOI 4.6 702:Crossing River
一本通里的翻译不够完整,OpenJudge中的英文原题中有对数据大小的限制:
样例组数
1
≤
T
≤
20
1\le T \le 20
1≤T≤20,人数
n
≤
1000
n \le 1000
n≤1000,每个人过河时间
s
≤
100
s \le 100
s≤100
【题目翻译】
描述
N个人想要渡河,只有一条最多能载两人的船。因此必须安排某种运送方案,让船在两岸间划来划去以使所有的人都能渡河。每人都有一个不同的划船速度,两人一同划船的速度取决于划船更慢的那个人。你的任务是确定一种策略,可以使这些人渡河的时间最少。
输入
输入的第1行包含一个单独的整数T(1<=T<=20),是测试数据的组数。然后是T组数据。每组数据的第一行是N,第二行包含N个整数,给出了每个人渡河的时间。总人数不多于1000人,没有人的渡河时间超过100秒。
输出
对于每组测试数据,打印一行,为N个人渡河所需的总秒数。
样例输入
1
4
1 2 5 10
样例输出
17
来源
POJ Monthly–2004.07.18
【题目考点】
1. 贪心
小船过河问题
同样问题可以参考:CSPJ2021初赛 第15题
【解题思路】
1. 思路
经典小船过河问题
考虑每次将2个人渡到对岸,有两种方案:
假设参与渡河的4人a、b、c、d的渡河时间分别为:
t
a
,
t
b
,
t
c
,
t
d
t_a,t_b,t_c,t_d
ta,tb,tc,td且有
t
a
≤
t
b
≤
t
c
≤
t
d
t_a\le t_b \le t_c \le t_d
ta≤tb≤tc≤td,要将c与d渡到对岸。
方案1:a作为船夫
渡河流程为:a与c到对岸,a回来,a与d到对岸,a回来。
渡河时间为渡河的二人中渡河时间较长的时间,所以该方案的渡河时间为:
2
t
a
+
t
c
+
t
d
2t_a+t_c+t_d
2ta+tc+td。
显然在该方案下,
t
a
t_a
ta越小渡河时间越短。所以a应该为渡河时间最短的人。
方案2:a与b作为船夫
渡河流程为:a与b到对岸,a回来,c与d到对岸,b回来。
该方案的渡河时间为:
2
t
b
+
t
a
+
t
d
2t_b+t_a+t_d
2tb+ta+td
显然在该方案下,
t
a
t_a
ta与
t
b
t_b
tb应该尽可能小,而
t
c
t_c
tc应该尽量接近
t
d
t_d
td。
其他将两个人渡到对岸的方案,都不如上述两种方案。这里不再做详细论证。
而上述两种方案的优劣是不固定的,所以需要每次判断哪种方案的渡河时间更短。
2. 具体做法
将渡河时间升序排序,选择渡河时间最短的两个人作为船夫a与b,每次选择渡河时间最长的两个人作为c与d进行渡河。比较两种渡河方案,看哪种方案渡河时间更短,选择渡河时间更短的渡河方案。
重复上述过程,每次渡过去两个人,直到剩余人数小于4。
如果剩下3人,按渡河时间从小到大分别为a,b,c,那么最佳的渡河方案为:a与c到对岸,a回来,a与b到对岸,渡河时间为:
t
a
+
t
b
+
t
c
t_a+t_b+t_c
ta+tb+tc。
如果剩下2人,则a,b两人可以直接渡到对岸,渡河时间为
t
b
t_b
tb。
如果剩下1人(【注意】如果一共只有1个人,就会出现这种情况。),那么渡河时间为
t
a
t_a
ta。
统计渡河总时间,输出。
【题解代码】
解法1:贪心
#include <bits/stdc++.h>
using namespace std;
#define N 1005
int main()
{
int n, m, t[N];
cin >> m;
while(m--)
{
int sum = 0;
int i;
cin >> n;
for(i = 1; i <= n; ++i)
cin >> t[i];
sort(t+1,t+1+n);
for(i = n; i >= 4; i-=2)
sum += min(2*t[1]+t[i]+t[i-1], t[1]+2*t[2]+t[i]);
if(i == 3)
sum += t[1]+t[2]+t[3];
else if(i == 2)
sum += t[2];
else//i == 1 如果只有一个人,那么就会出现这种情况
sum += t[1];
cout << sum << endl;
}
return 0;
}