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;
}