评测链接:http://www.luogu.org/problem/show?pid=1594
题目描述 Description
护卫车队在一条单行的街道前排成一队,前面河上是一座单行的桥。因为街道是一条单行道,所以任何车辆都不能超车。桥能承受一个给定的最大承载量。为了控制桥上的交通,桥两边各站一个指挥员。护卫车队被分成几个组,每组中的车辆都能同时通过该桥。当一组车队达到了桥的另一端,该端的指挥员就用电话通知另一端的指挥员,这样下一组车队才能开始通过该桥。每辆车的重量是已知的。任何一组车队的重量之和不能超过桥的最大承重量。被分在同一组的每一辆车都以其最快的速度通过该桥。一组车队通过该桥的时间是用该车队中速度最慢的车通过该桥所需的时间来表示的。问题要求计算出全部护卫车队通过该桥所需的最短时间值。
输入输出格式 Input/output
输入格式:
输入文件第一行包含三个正整数(用空格隔开),第一个整数表示该桥所能承受的最大载重量(用吨表示);第二个整数表示该桥长度(用千米表示);第三个整数表示该护卫队中车辆的总数(n<1000)。接下来的几行中,每行包含两个正整数W和S(用空格隔开),W表示该车的重量(用吨表示),S表示该车过桥能达到的最快速度(用千米/小时表示)。车子的重量和速度是按车子排队等候时的顺序给出的。
输出格式:
输出文件应该是一个实数,四舍五入精确到小数点后1位,表示整个护卫车队通过该桥所需的最短时间(用分钟表示)。
输入输出样例 Sample input/output
输入样例:
100 5 10
40 25
50 20
50 20
70 10
12 50
9 70
49 30
38 25
27 50
19 70
输出样例:
75.0
解析:动态规划+rmq;
首先,所有车辆是排成一列通过该桥,这个顺序是不变的,我们就可以把车辆看成一条线上的有序点,这个时候就应当想到是否能用动态规划来解决这道题,并向这个方面思考。
我们用 f[i] 表示前 i 辆车通过大桥的最短时间,v(i,j) 表示从 第 i 辆到第 j 辆车之间(包括i,j)的最小行驶速度,zo(i,j)表示从 i 到 j 辆车的总重,weight表示桥的承受重量,那么大致思路如下:
for(i=1;i<=n;i++)
for(j=i-1;j>=1;j--)
{
f[i]=length/v[i];(最后一组只包含 i 车 );
if(zo(j,i)>weight)break;(若第 j 辆车加入最后一组车队,能否通过大桥)
}
具体的思路如上,代码实现就看个人了。
关于求v(i,j),也就是求 第 i 辆到第 j 辆车之间(包括i,j)的最小行驶速度,可以用线段树,也可以用更简单的st算法(我在这里用的是st算法)。
代码:
#include<cstdio>
#include<algorithm>
#define maxn (1000+100)
using namespace std;
int weight,length,n;
int a[20],low[maxn][20],w[maxn];
double f[maxn];
void readdata()
{
scanf("%d%d%d",&weight,&length,&n);
int i,j,k;
for(i=1;i<=n;i++)scanf("%d%d",&w[i],&low[i][0]);
for(i=a[0]=1;i<=10;i++)a[i]=a[i-1]*2;
low[0][0]=100000000;
for(j=1;j<=10;j++)
for(i=0;i<=n-a[j]+1;i++)
low[i][j]=min(low[i][j-1],low[i+a[j-1]][j-1]);
}
inline int getmin(int l,int r)
{
int xiao=100000000,i;
for(i=9;i>=0;i--)if(l+a[i]-1<=r)return min(low[l][i],low[r-a[i]+1][i]);
}
void work()
{
int i,j,k,zo;
f[0]=0;
for(i=1;i<=n;i++)
{
zo=w[i];f[i]=f[i-1]+((double)length)/low[i][0];
for(j=i-1;j>=1;j--)
if(zo>weight-w[j])break;
else f[i]=min(f[i],f[j-1]+((double)length)/getmin(j,i)),zo+=w[j];
}
f[n]*=60;
printf("%.1lf\n",f[n]);
}
int main()
{
readdata();
work();
return 0;
}