hloj#402 护卫队解题讨论

                               护卫队

题目描述

护卫车队在一条单行的街道前排成一队,前面河上是一座单行的桥。因为街道是一条单行道,所以任何车辆都不能超车。桥能承受一个给定的最大承载量。

为了控制桥上的交通,桥两边各站一个指挥员。护卫车队被分成几个组,每组中的车辆都能同时通过该桥。当一组车队到达了桥的另一端,该端的指挥员就用电话通知另一端的指挥员,这样下一组车队才能开始通过该桥。

每辆车的重量是已知的。任何一组车队的重量之和不能超过桥的最大承重量。被分在同一组的每一辆车都以其最快的速度通过该桥。一组车队通过该桥的时间是用该车队中速度最慢的车通过该桥所需的时间来表示的。

问题要求计算出全部护卫车队通过该桥所需的最短时间值。

输入格式

输入文件第一行包含三个正整数(用空格隔开),第一个整数表示该桥所能承受的最大载重量(用吨表示); 第二个整数表示该桥的长度(用千米表示); 第三个整数表示该护卫队中车辆的总数(n<1000)

接下来的几行中,每行包含两个正整数WS(用空格隔开),W表示该车的重量(用吨表示),S表示该车过桥能达到的最快速度(用千米/小时表示)。 车子的重量和速度是按车子排队等候时的顺序给出的。

输出格式

输出文件应该是一个实数,四舍五入精确到小数点后1位,表示整个护卫车队通过该桥所需的最短时间(用分钟表示)。

样例数据

input

100 5 10
40 25
50 20
50 20
70 10
12 50
9 70
49 30
38 25
27 50
19 70

output

75.0

数据规模与约定

保证所有数据都在 int 范围内,但是中间和输出结果可能超 int

时间限制:1s

空间限制:256MB

 

----------------------------------------------------我是华丽的分割线-------------------------------------------------------

 

显然是一道线性DP题.

 

状态转移式要保证

 1.无后效性

 2.最优子结构.

 首先审一下题意,得出以下结论:

            1.所有车无论怎么分组,不用考虑排序

                        2.任何一组车队的重量之和不能超过桥的最大承重量(边界条件)

                        3.一组车队通过该桥的时间是用该车队中速度最慢的车通过该桥所需的时间来表示的

 

从结论3我们可以知道:选取的车应该是在某一子问题中满足重量要求的所花时间的最大值

我们要注意精度,f[]必须用double双精度类型的,先预处理,存储车的重量以及速度和它过桥的时间。

用w[i][j]存储i~j辆车过桥的时间,sum[i][j]存储i~j辆车过桥的总重量.

因为w[i][j]表示的是i~j辆车过桥的时间,因此w[i][j]满足:

w[i][j]=max(w[i][j-1],a[j].t);即新来的车与原组车过桥时间进行比较,更新w[i][j];

同样,sum[i][j]表示的是第i辆车到第j辆车的重量总和,为了表示sum[i][j],

我们用一个前缀和sum[1][i]存储第1辆车到第i辆车的重量和。

 

for(int i=1;i<=n;i++)
    sum[1][i]=sum[1][i-1]+a[i].w;//a[i].w表示第i辆车的重量

 


		
	

 

 因此sum[i][j]满足:

  sum[i][j]=sum[1][j]-sum[1][i]+a[i].w  

即线段差分的方法求出.

预处理sum[i][j]和w[i][j],即为:

 

for(int i=1;i<=n;i++)
{
    w[i][i]=a[i].t;//a[i].t表示第i辆车过桥所需的时间
    for(int j=i+1;j<=n;j++)
    {
        w[i][j]=max(w[i][j-1],a[j].t);
        sum[i][j]=sum[1][j]-sum[1][i]+a[i].w;
    }
}

 

 

由题意,该问题的边界为:sum[i][j]<=wei       //wei表示桥的最大承重量

在边界条件下,由此我们推出状态转移式:

    f[i]=min(f[i],(f[i-k]+w[i-k+1][i]));

用两重循环,外层循环枚举i,内层循环枚举k。

 


for(int i=2;i<=n;i++)
    for(int k=i;k>=1;k--)
        if(sum[i-k+1][i]<=wei)
            f[i]=min(f[i],f[i-k]+w[i-k+1][i]);

基本就是这样了,详细见代码,别忘了最后保留一位小数哦!

以下为代码:


#include<bits/stdc++.h>
using namespace std;
long long n,wei,l;
double f[10010];
long long sum[1010][1010];
double w[1010][1010];
struct che
{
    long long w,s;
    double t;
}a[100100];
int main()
{
    freopen("guard.in","r",stdin);
    freopen("guard.out","w",stdout);
    memset(w,0,sizeof(w));
    memset(sum,0,sizeof(sum));
    scanf("%lld%lld%lld",&wei,&l,&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&a[i].w,&a[i].s);
        a[i].t=(double(l)/double(a[i].s))*60.0;
        sum[1][i]=sum[1][i-1]+a[i].w;
        f[i]=828720169801180.0;
    }
    w[1][1]=a[1].t;
    for(int i=1;i<=n;i++)
    {
        w[i][i]=a[i].t;
        for(int j=i+1;j<=n;j++)
        {
            w[i][j]=max(w[i][j-1],a[j].t);
            sum[i][j]=sum[1][j]-sum[1][i]+a[i].w;
        }
    }
    f[1]=a[1].t;f[0]=0.0;
    for(int i=2;i<=n;i++)
    {
        for(int k=i;k>=1;k--)
        {
            if(sum[i-k+1][i]<=wei)
                f[i]=min(f[i],f[i-k]+w[i-k+1][i]);
        }
    }
    printf("%.1lf\n",f[n]);
    return 0;
}

其实这道题还可以存储速度作为参数,最后进行一个处理,也是一个比较好的解法。这里不再详细描述。

主要考察了DP的理解和运用,比较有深意。
谢谢阅读!
posted @ 2019-03-09 20:09  smilke  阅读(718)  评论(0编辑  收藏  举报