1. 程式人生 > >【杭電100題】【DP】2059 龜兔賽跑

【杭電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時就得到了“從起點到終點”的最小時間。