[Luogu P4469] [BZOJ 1075] [SCOI2007]最优驾车

洛谷传送门
BZOJ传送门

题目描述

n n n条南北方向的双向街道和 n n n条东西方向的双向街道纵横交错。相邻街道(不管是哪个走向)的距离均为 L L L英里。西南角交叉口的坐标为 ( 1 , 1 ) (1,1) (1,1),东北角为 ( n , n ) (n,n) (n,n)。在所有交叉口均可任意改变行驶方向。每条街道有它自己的最高速度限制,该限制对整条街道有效(不管行驶方向如何)。

你的任务是从交叉口 ( x s , y s ) (x_s,y_s) (xs,ys)开车行驶到 ( x t , y t ) (x_t,y_t) (xt,yt),要求只能在交叉口处改变速度,行驶过程中不得违反所在街道的速度限制,只能沿着路程最短的线路行驶,并且行驶时间在给定的闭区间 [ t 1 , t 2 ] [t_1,t_2] [t1,t2]内。车速以“每小时英里数”为单位,它必须是 5 5 5的正整数倍。若车速为 v v v,则每加仑汽油能行驶的英里数为 80 − 0.03 v 2 80-0.03v^2 800.03v2

输入输出格式

输入格式:

输入第一行为两个整数 n , L n, L n,L
第二行包含 n n n个正整数,从南到北描述 n n n条东西走向的街道的速度限制。
第三行包含 n n n个正整数,从西到东描述 n n n条南北走向的街道的速度限制。
第四行包含六个正整数 x s , y s , x t , y t , t 1 , t 2 x_s, y_s, x_t, y_t, t_1, t_2 xs,ys,xt,yt,t1,t2

输出格式:

如果无解,输出No

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

输入输出样例

输入样例#1:
6 20
30 40 50 50 50 50
50 50 50 50 50 40
1 1 6 6 300 320
输出样例#1:
300 6.25
318 5.60
输入样例#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
输出样例#2:
No

说明

样例 1 1 1的最快路线为以 40 40 40英里/小时为速度匀速前进,路程为 200 200 200英里,因此时间为 5 5 5小时,每加仑汽油可以行驶 80 − 0.03 ∗ 40 ∗ 40 = 32 80-0.03*40*40=32 800.034040=32英里,因此耗油量为 200 / 32 = 6.25 200/32=6.25 200/32=6.25加仑。

最省油路线是先以 40 40 40英里/小时行驶 120 120 120英里,然后以 35 35 35英里/小时行驶 80 80 80英里,耗油量为 120 / 32 + 80 / ( 80 − 0.03 ∗ 35 ∗ 35 ) = 5.60 120/32+80/(80-0.03*35*35)=5.60 120/32+80/(800.033535)=5.60加仑。下图的路线可以同时满足两种方案(其中第二种方案需要在 ( 6 , 2 ) (6,2) (6,2)处改变速度)。

img

20%的数据满足: n ≤ 4 n\le 4 n4

50%的数据满足: n ≤ 8 n\le 8 n8

100%的数据满足: 1 ≤ n ≤ 10 , 1 ≤ l ≤ 20 , 0 ≤ t 1 ≤ t 2 ≤ 1000 1\le n\le 10, 1\le l\le 20, 0\le t_1\le t_2\le 1000 1n10,1l20,0t1t21000. 速度限制不超过 50 50 50

解题分析

思路很妙妙的 D P DP DP

很容易看出, 如果我们设 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]为在 x x x方向走了 i i i步, 在 y y y方向走了 j j j步, 油量或时间用了 k k k的最优时间/油量, 都可以转移,问题在于 k k k是一个实数, 无法记录这一维状态。

考虑转化一下时间: 我们要求先把速度 ÷ 5 \div5 ÷5, 使速度变为 1 → 10 1\to10 110的正整数, 最后把时间 ÷ 5 \div 5 ÷5就可以转化回来了。 然后我们想记录的时间实际上是 ( 1 v 1 + 1 v n + . . . + 1 v n ) L (\frac{1}{v_1}+\frac{1}{v_n}+...+\frac{1}{v_n})L (v11+vn1+...+vn1)L, 先统一乘一个 L C M ( 1 , 2 , 3 , . . , 10 ) LCM(1,2,3,..,10) LCM(1,2,3,..,10) ,就可以转化为一个整数的状态, 最后再除回来就好了。

注意题目问的是分钟, 给的是小时, 乘上60。

总复杂度 O ( n 3 L C M ∗ M a x s p e e d ) O(n^3LCM*Maxspeed) O(n3LCMMaxspeed)

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define EPS 1e-8
#define INF 1e20
#define gc getchar()
#define ll long long
#define db double
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
template <class T> IN T abs(T a) {return a > 0 ? a : -a;}
const int LCM = 2520;
const int UP = 2 * 10 * LCM;
int n, L, t1, t2;
int dx, dy, sx, sy, tx, ty, delx, dely;
int limx[15], limy[15], tcost[15];
db dp[11][11][UP + 1];
IN db calc(R int v) {return 80 - 0.03 * v * v;}
int main(void)
{
	R int i, j, k, v, cx, cy, nxx, nxy;
	scanf("%d%d", &n, &L);
	for (i = 0; i < n; ++i) scanf("%d", &limx[i]);//lr
	for (i = 0; i < n; ++i) scanf("%d", &limy[i]);//ud
	scanf("%d%d%d%d%d%d", &sx, &sy, &tx, &ty, &t1, &t2);
	sx--, sy--, tx--, ty--;
	delx = abs(tx - sx), dely = abs(ty - sy);
	dx = tx - sx < 0 ? -1 : 1, dy = ty - sy < 0 ? -1 : 1;
	for (R int i = 1; i <= 10; ++i) tcost[i] = LCM / i;
	for (i = 0; i <= delx; ++i)
	for (j = 0; j <= dely; ++j)
	for (k = 0; k <= UP; ++k)
	dp[i][j][k] = INF;
	dp[0][0][0] = 0;
	for (i = 0; i <= delx; ++i)
	for (j = 0; j <= dely; ++j)
	for (k = 0; k <= UP; ++k) if (dp[i][j][k] < INF)
	{
		cx = i + 1, cy = j;
		if (cx <= delx)
		{
			nxx = sx + cx * dx, nxy = sy + cy * dy;
			for (v = 1; v * 5 <= limx[nxy] && tcost[v] + k <= UP; ++v)
			dp[cx][cy][tcost[v] + k] = min(dp[cx][cy][tcost[v] + k], dp[i][j][k] + 1.0 * L / calc(5 * v));
		}
		cx = i, cy = j + 1;
		if (cy <= dely)
		{
			nxx = sx + cx * dx, nxy = sy + cy * dy;
			for (v = 1; v * 5 <= limy[nxx] && tcost[v] + k <= UP; ++v)
			dp[cx][cy][tcost[v] + k] = min(dp[cx][cy][tcost[v] + k], dp[i][j][k] + 1.0 * L / calc(5 * v));
		}
	}
	int mntim = -1, mnoil = -1;
	for (i = 0; i <= UP; ++i) if (dp[delx][dely][i] != INF)
	{
		db tim = 1.0 * i * L * 12 / LCM;
		if (tim >= t1 && tim <= t2)
		{
			if (mntim == -1) mntim = i;
			if (mnoil == -1) mnoil = i;
			else if (dp[delx][dely][i] < dp[delx][dely][mnoil]) mnoil = i;
		}
	}
	if (mntim == -1) return puts("No"), 0;
	printf("%d %.2lf\n", (int)std::ceil(1.0 * mntim * L * 12 / LCM), dp[delx][dely][mntim]);
	printf("%d %.2lf\n", (int)std::ceil(1.0 * mnoil * L * 12 / LCM), dp[delx][dely][mnoil]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值