两人过桥(bridge.???)
问题描述:
有 n 个人希望在晚上通过一座桥。在任何时刻,最多只能有两个人在桥上,并且必须
要带着手电筒才能通过桥。现在的麻烦是只有一个手电筒,所以必须安排某种顺序,使得手
电筒可以被带回去让更多的人过桥(手电筒必须由人带回,不可以从对岸扔过去)。
每个人都有不同的过桥时间,两个人一起过桥所用的时间等于其中较慢的一个。你的
任务是要找出能在最短时间内使所有人都过桥的方案。
输入格式:
第一行是一个整数 n。
接下来有 n 行,每一行给出一个人的过桥时间(整数,单位:秒)。
每个人的过桥时间不超过 100 秒。
输出格式:
输出一行一个数,表示所有人过桥的最短时间。
样例输入:
4
1
2
5
10
样例输出:
17
样例解释:
可以先让 1 和 2 过桥,然后 1 回来,让 5 和 10 过桥,然后 2 再回来带 1 一起过桥,时
间为:2+1+10+2+2=17。
数据限制:
40%的数据满足:n<=100;
100%的数据满足:n<=1000。
分析
贪心法
最简单的一个贪心策略是:让一个最快的人来回带人
但是显然是错误的比如4个人:1 1 100000 100000
最快的来回带的话要:1+1+100000+1+100000=200003
但是如果先将1 1运过去的话,然后1回来,再让100000 100000一起过去
再让右边的1来回一趟,就只要1+1+100000+1+1=100004,这样显然小了
所以第一种贪心的策略显然是不合理的,下面换种贪心策略:
首先,慢的肯定是过了桥之后不回来了
就上面那种情况,我们就是先将最快的两个带过去,
然后快的一个过来,让两个慢的过去,然后让快的再回来,……
但是这种贪心策略也不优,因为:
如果是1 10000 10000 10000,答案又不对了(还是第一种策略优)
结合以上两点,对于最慢的两个人我们有两种处理方法就是:
1、让最快的人来回带
2、让最快的两个人过去,再让最慢的两个一起过去,这样就减少了最慢的重复计算
关于这个贪心策略的证明是:
首先,过桥速度排在第三名之后的人不可能担任送回手电筒的任务,原因是不如速度第一和第二的人送回来得高效。这样,
当前左岸速度最慢的人过桥后就不可能再回来,那么我们可以优先让速度慢的过河,因为其不可能返回,先过后过等效。如此一来,就可以得到上述的贪心策略。
程序:
var
a:array[0..12000] of longint;
n,i:longint;
ans,x,y:int64;
procedure kp(l,r:longint);
var
i,j,mid:longint;
begin
if l>=r then exit;
i:=l;j:=r;mid:=a[(i+j) div 2];
repeat
while a[i]<mid do inc(i);
while a[j]>mid do dec(j);
if i<=j then
begin
a[0]:=a[i];a[i]:=a[j];a[j]:=a[0];
inc(i);dec(j);
end;
until i>j;
kp(l,j);
kp(i,r);
end;
begin
assign(input,'bridge.in');
assign(output,'bridge.out');
reset(input);
rewrite(output);
readln(n);
for i:=1 to n do
read(a[i]);
kp(1,n);
ans:=0;
i:=n;
while true do
begin
if i=2 then ans:=ans+a[2];
if i=3 then ans:=ans+a[1]+a[2]+a[3];
if i<=3 then break;
x:=2*a[2]+a[1]+a[i];
y:=2*a[1]+a[i]+a[i-1];
if x<y then ans:=ans+x else ans:=ans+y;
i:=i-2;
end;
write(ans);
close(input);
close(output);
end.