【杭电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时就得到了“从起点到终点”的最小时间。

猜你喜欢

转载自blog.csdn.net/qq_41727666/article/details/83958520
今日推荐