【杭電100題】【DP】2059 龜兔賽跑
Problem Description
兔子能夠毫不休息得以恆定的速度(VR m/s)一直跑。
烏龜不惜花下血本買了最先進的武器——“"小飛鴿"牌電動車。這輛車在有電的情況下能夠以VT1 m/s的速度“飛馳”,可惜電池容量有限,每次充滿電最多隻能行駛C米的距離,以後就只能用腳來蹬了,烏龜用腳蹬時的速度為VT2 m/s。更過分的是,烏龜竟然在跑道上修建了很多很多(N個)的供電站,供自己給電動車充電。其中,每次充電需要花費T秒鐘的時間。當然,烏龜經過一個充電站的時候可以選擇去或不去充電。
比賽馬上開始了,兔子和帶著充滿電的電動車的烏龜並列站在起跑線上。你的任務就是寫個程式,判斷烏龜用最佳的方案進軍時,能不能贏了一直以恆定速度奔跑的兔子。
Input
本題目包含多組測試,請處理到檔案結束。每個測試包括四行:
第一行是一個整數L代表跑道的總長度
第二行包含三個整數N,C,T,分別表示充電站的個數,電動車衝滿電以後能行駛的距離以及每次充電所需要的時間
第三行也是三個整數VR,VT1,VT2,分別表示兔子跑步的速度,烏龜開電動車的速度,烏龜腳蹬電動車的速度
第四行包含了N(N<=100)個整數p1,p2...pn,分別表示各個充電站離跑道起點的距離,其中0<p1<p2<...<pn<L
其中每個數都在32位整型範圍之內。
Output
當烏龜有可能贏的時候輸出一行 “What a pity rabbit!"。否則輸出一行"Good job,rabbit!";
題目資料保證不會出現烏龜和兔子同時到達的情況。
#include <iostream> #include <float.h> #include <cstring> using namespace std; int l; //跑道長度 int n, c, t; //充電站個數,距離,時間 int vr, vt1, vt2; //兔子速度,烏龜車速,烏龜腳速 int p[102]; double memo[102]; double tr, tt; //兔子用時,烏龜用時 void input(); void dp(); int main() { while(scanf("%d", &l) != EOF) { input(); dp(); tr=1.0*l/vr; tt=memo[n+1]; if(tr<tt) { printf("Good job,rabbit!\n"); } else { printf("What a pity rabbit!\n"); } } return 0; } void input() { cin>>n>>c>>t; cin>>vr>>vt1>>vt2; p[0]=0; for(int i=1; i<=n; i++) { cin>>p[i]; } p[n+1]=l; } void dp() { memo[0]=0.0; //注意此處一定是double型 double tmp; for(int i=1; i<=n+1; i++) //計算從起點滿電到達第i站的時間 { memo[i]=DBL_MAX; for(int j=0; j<i; j++) //計算從起點經第j站加滿電到達第i站的時間 { tmp=(double)memo[j]; //先從起點到達第j站 if(j!=0) //若第j站不是起點 { tmp+=(double)t; //則在第j站充滿電再出發 } } int length=p[i]-p[j]; if(length<=c) //若能從第j站直接騎電動車到達第i站 { tmp+=1.0*length/vt1; } else //若不能直接電動到達,還需腳踏一段時間 { tmp+=1.0*c/vt1; //騎電動車時間 tmp+=(length-c+0.0)/vt2; //腳踏車時間 } memo[i]=min(memo[i], tmp); //記錄最小的從起點到達第i站的時間 } } }
【2018/11/11後記】
1.參考部落格:杭電ACM2059——龜兔賽跑~~DP
2.此題是旅遊預算/加油站問題的稍微進階版,
在旅遊預算問題中,我採取了遞迴+備忘錄的方式,dp(i)代表從第i個加油站到達終點的最小花費,最終得到從起點(i=0時)到達終點的最小花費;
在本題中,我採取了自底向上的計算方式,不再遞迴,通過計算從起點到達第i個充電站的最小時間,最終得到從起點到達終點(i=n+1時)的最小時間;
3.我們用p陣列記錄總共n個充電站距離起點的長度,其中p【0】=0代表起點,p【n+1】=L代表終點。
4.如何計算從起點到達第i個充電站的時間呢?
首先,i=0時,從起點到達起點,顯然時間為0,即memo【0】=0.0
其次,我們可以通過變數j,將【從起點到第i個充電站】這個問題,分成【從起點到達第j個充電站】+【在第j個充電站充滿電,再直奔第i個充電站】兩個子問題(直奔的意思是途中不充電);
子問題①:【從起點到第j個充電站】,因為我們是自底向上計算,所以這個子問題的解我們肯定在前面的計算裡已經得出了,並記錄在memo【j】裡了,直接使用這個解就好;
子問題②:【在第j個充電站充滿電,再出發到達第i個充電站】,這裡需要理解一個問題:我們需要在第j個充電站花費時間t充滿電再出發(除非j=0,在起點不用花時間充電)。
可能有人會想:不充電行不行呢?如果在第j個充電站不充電的話,也就是車直接路過第j個充電站而沒有停留,這種情況其實被包含在別的子問題裡面(比如當我們在第j-1個充電站充電後直奔第i個充電站,這時就包含了“從第j個充電站路過不停留”這種情況);
在我們這個子問題裡,車必須在第j個充電站停留並充電;然後需要判斷能否以電動速度到達第i個充電站,如果可以,就用“距離”除以“電動速度”得到“電動時間”;如果到達不了,就以電動速度行駛最大距離c,計算一下“電動時間”,剩下的距離以腳踏速度行駛,計算一下“腳踏時間”;
遍歷每一個j,把各種路線的“從起點到第i個充電站”的最小時間求出來,再i++,繼續遍歷j;
最後當i=n+1時就得到了“從起點到終點”的最小時間。