【貪心策略】渡河(river)
“假舟楫者,非能水也,而絕江河。”這句話說的是,借助渡船的人,不是會遊水,卻能橫渡江河。
會遊水的人反而不一定能順利地橫渡江河。由於江面風浪很大,他們必須潛泳渡河。這就必須用到氧氣瓶。氧氣瓶當然是出題人買的,而出題人沒錢,所以只買了一個。這種氧氣瓶有兩個輸出氧氣的管道,最多可供兩個人同時過河;其中的氧氣是無限的。
顯然每次應該有兩個人過河,再派對岸的一個人把氧氣瓶送回來。需要註意的是,已經橫渡到對岸的所有隊員都可以送回氧氣瓶。
現在給定你每個人渡河所需的時間,要你求出,按照以上方案把所有人送到對岸,所需的最短時間。兩個人一起過河的時候,所需的時間等於慢的人所用時間。
【輸入文件】第一行,一個正整數
其余n個正整數,在第2行,相鄰兩個整數之間用一個空格隔開。
【輸出文件】一行1個整數,表示所用的最短時間。
【樣例輸入】
3
1 3 4
【樣例輸出】8
【數據規模和約定】
對於20%的數據,n<=10
對於100%的數據,文件中的所有整數<=1000
【題解】
本題非常的奇怪,一拿到題目首先想到的貪心方法就是以1號做中轉,每次由1號送一個人到對岸,再從對岸送回氧氣瓶。
典型的例子是這組數據: 1 2 9 9
最優的方法是1與2號過去,1號回來,3與4號過去,2號回來,1與2號過去。這個方案花費是16。如果按照原先的方案,花費是22。
但是,絕對的存在反例!
典型的例子是這組數據:1 9 9 9
原先的方案最佳(29),新方案反而差(37)。
經過仔細觀察,我們發現一個事實,對岸的人只有兩個過河時間最小的人有意義。
這裏的意義實質上是由兩個過河時間最小的人來決定最優解。
假定現在我們現在需要挨個過河,有2+2=4個人;
下標 分別是1 2 i-1 i
有兩種方法:
①1號自己把兩個人帶過去。
1 i(1和i共用氧氣筒) 1(1送還氧氣筒) 1 i-1(1把i-1送到對岸) 1(再回來準備下一個)
用時為下標為 i 1 i-1 1的時間之和
②1號回來,兩個人一起去對岸,2號回來以後再跟1號一起回去。
1 2(1 2一起去對岸) 1(1回來送氧氣筒) i i-1
用時為下標為 2 1 i 2的時間之和
所以記t1=a[1]+a[2]+a[i-1]+a[i];t2=a[2]+a[1]+a[i]+a[2];
sum=sum+min(t1,t2);
接下來分奇偶討論!
當渡河人數為偶數時,偶數-2=偶數
不妨設n恰好為4時,只需要渡河一次,
按照上訴2種方法,①可行,②扯淡,此時②必然會回到本岸,所以還要回去,sum=sum+a[2]
當渡河人數為奇數時,奇數-2=奇數
當n恰好為3時,就是1 2 3的渡河方法,最快的 1 3(1 3去) 1(回來送氧氣筒) 1 2(1 2去)
時間是 3 1 2= a[1]+a[2]+a[3] sum=sum+a[1]+a[2]+a[3];
所以程序就非常簡單:
var t1,t2,n,sum,i:longint; a:array[1..1000]of longint; procedure qsort(l,r:longint); var t,i,j,mid:longint; begin i:=l; j:=r; mid:=a[(l+r)div 2]; while i<j do begin while a[i]<mid do inc(i); while a[j]>mid do dec(j); if i<=j then begin t:=a[i]; a[i]:=a[j]; a[j]:=t; inc(i);dec(j); end; end; if l<j then qsort(l,j); if r>i then qsort(i,r); end; begin assign(input,‘river.in‘); assign(output,‘river.out‘); reset(input); rewrite(output); readln(n); for i:=1 to n do read(a[i]); qsort(1,n); if n mod 2=1 then sum:=a[1]+a[2]+a[3] else sum:=a[2]; i:=n; while i>3 do begin t1:=a[2]+a[1]+a[i]+a[2]; t2:=a[i]+a[1]+a[i-1]+a[1]; if t1>t2 then t1:=t2; sum:=sum+t1; i:=i-2; end; writeln(sum); close(input); close(output); end.
【貪心策略】渡河(river)