BZOJ 1075 最优驾车drive (dp 分数转整)

本文解析了一道名为“最优驾车”的SCOI2007竞赛题目,涉及在一个由交错街道组成的网格中寻找从起点到终点的最短路径与最省油路径。文章详细介绍了输入输出格式、示例及其解释,并给出了一段C++代码实现,通过动态规划的方法解决此问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1075: [SCOI2007]最优驾车drive

Time Limit: 20 Sec Memory Limit: 162 MB
Description

  有n条南北方向的双向街道和n条东西方向的双向街道纵横交错。相邻街道(不管是哪个走向)的距离均为L英
里。西南角交叉口的坐标为(1,1),东北角为(n,n)。在所有交叉口均可任意改变行驶方向。每条街道有它自己的最
高速度限制,该限制对整条街道有效(不管行驶方向如何)。你的任务是从交叉口(xs,ys)开车行驶到(xt,yt),要
求只能在交叉口处改变速度,行驶过程中不得违反所在街道的速度限制,只能沿着路程最短的线路行驶,并且行驶
时间在给定的闭区间[t1,t2]内。车速以“每小时英里数”为单位,它必须是5的正整数倍。若车速为v,则每加仑
汽油能行驶的英里数为80-0.03v2。

Input

  输入第一行为两个整数n, L, 第二行包含n个正整数,从南到北描述n条东西走向的街道的速度限制,第三行包
含n个正整数,从西到东描述n条南北走向的街道的速度限制。第四行包含六个正整数xs, ys, xt, yt, t1, t2.

Output

  如果无解,输出No,否则输出两行,分别描述最早到达的方案(若有多种方案,选择其中最省油的)和最省油
的方案(如果有多种方案,选择其中最早到达的)。每种方案用两个数表示,第一个数表示到达时刻(单位:分钟
,向上取整);第二个数表示耗油量(单位:加仑,四舍五入保留两位小数)。

Sample Input

【样例输入1】

6 20
30 40 50 50 50 50
50 50 50 50 50 40
1 1 6 6 300 320

【样例输入2】

8 2
10 20 20 30 10 20 10 10
10 20 20 30 10 20 10 20
6 8 2 4 10 39

Sample Output

【样例输出1】

300 6.25
318 5.60

【样例输出2】

No

【样例说明】样例1的最快路线为以40英里/小时为速度匀速前进,路程为200英里,因此时间为5小时,每加仑汽油可以行驶80-0.03*40*40=32英里,因此耗油量为200/32=6.25加仑。最省油路线是先以40英里/小时行驶120英里,然后以35英里/小时行驶80英里,耗油量为120/32+80/(80-0.03*35*35)=5.60加仑。下图的路线可以同时满足两种方案(其中第二种方案需要在(6,2)处改变速度)。【限制】100%的数据满足:1<=n<=10, 1<=l<=20, 0<=t1<=t2<=1000. 速度限制不超过50

思路:
大佬题解,细节上有代码注释

#include <cstdio>
#include <iostream>
#define eps 1e-8
using namespace std;

const int N = 12, M = 210010;
const double INF = 1e15;

int n, L, lcm, lim;
int a[N], b[N], xs, ys, xt, yt, t1, t2, ans1 = -1, ans2;
double f[2][N][M], w[N];

int gcd(int a, int b){
    return b ? gcd(b, a % b) : a;
}

int recover(int x){//转回正常 
    x *= 12;
    return x / lcm + (x % lcm > 0);
}

int main(){
    scanf("%d%d", &n, &L);
    for(int i=1; i<=10; i++) w[i] = 1.0 * L / (80.0 - 0.75 * i * i);//i除以了5,所以0.03乘上25 
    for(int i=1; i<=n; i++) scanf("%d", &a[i]), a[i] /= 5;
    for(int i=1; i<=n; i++) scanf("%d", &b[i]), b[i] /= 5;
    int cc = 0; //最大speed 
    for(int i=1; i<=n; i++){
        if(cc < a[i]) cc = a[i];
        if(cc < b[i]) cc = b[i];
    }
    for(int i=lcm=1; i<=cc; i++) lcm = lcm * i / gcd(lcm, i);//保证除以任何v都是整数 
    scanf("%d%d%d%d%d%d", &xs, &ys, &xt, &yt, &t1, &t2);
    lim = t2 * lcm / 12;//v除以了5,但是L没变,所以time就要*5,但是考虑到要转成hour(v是以hour为单位的),就要/60,所以/12 
    if(xs > xt) swap(xs, xt), swap(ys, yt);
    if(ys > yt){
        for(int i=1, j=n; i<j; i++, j--) swap(a[i], a[j]);
        ys = n - ys + 1, yt = n - yt + 1;
    }//把路径调整到从左下角到右上角 
    for(int j=ys; j<=yt; j++)
        for(int k=0; k<=lim; k++)
            f[0][j][k] = INF;
    f[0][ys][0] = 0;
    int p = 0;
    for(int i=xs; i<=xt; i++, p^=1){
        for(int j=ys; j<=yt; j++) 
            for(int k=0; k<=lim; k++) 
                f[p^1][j][k] = INF;//滚动数组清空 
        for(int j=ys; j<=yt; j++)
            for(int k=0; k<=lim; k++)
                if(f[p][j][k] < INF){
                    if(j < yt)
                    for(int x=b[i]; x; x--){//速度可以取b[i]及以下 
                        int t = k + lcm / x * L;//把 L / x 替换为 lcm / x * L 时间还是成比例的 
                        if(t <= lim)
                            f[p][j+1][t] = min(f[p][j+1][t], f[p][j][k] + w[x]);
                    }
                    if(i < xt)
                    for(int x=a[j]; x; x--){
                        int t = k + lcm / x * L;
                        if(t <= lim)
                            f[p^1][j][t] = min(f[p^1][j][t], f[p][j][k] + w[x]);
                    }
                }
    }
    for(int k=0; k<=lim; k++)
        if(k * 12 >= t1 * lcm && f[p^1][yt][k] < INF){//t1=L/v,替换为t1=lcm/v*L 相当于t1变为t1*lcm 
            if(ans1 < 0) ans1 = k;
            if( !ans2 || f[p^1][yt][k] + eps < f[p^1][yt][ans2] ) ans2 = k;
        }
    if(ans1 < 0) { printf("No"); return 0;}
    printf("%d %.2f\n%d %.2f", recover(ans1), f[p^1][yt][ans1], recover(ans2), f[p^1][yt][ans2]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值