洛谷传送门
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 80−0.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 80−0.03∗40∗40=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/(80−0.03∗35∗35)=5.60加仑。下图的路线可以同时满足两种方案(其中第二种方案需要在 ( 6 , 2 ) (6,2) (6,2)处改变速度)。
20%的数据满足: n ≤ 4 n\le 4 n≤4
50%的数据满足: n ≤ 8 n\le 8 n≤8
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 1≤n≤10,1≤l≤20,0≤t1≤t2≤1000. 速度限制不超过 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 1→10的正整数, 最后把时间 ÷ 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(n3LCM∗Maxspeed)。
代码如下:
#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]);
}