NOIP2018普及组摆渡车题解

题解又双叒叕来了!

每天一个小题解,你学废了吗?

其实我刚开始看题时,是酱紫的

我想我大概是废了。

肯定是要对t 排序的。

最初想法肯定是f[i] 表示前i个人全上车或到站的最小等待时间。

但你能够发现如果不给出上一班车的时间的话f 根本转移不了。

那就f[i][j] 表示最后一班车j 时开出的最小等待时间?

数据范围打人脸,于是我就去查题解了。

但是我们发现实际上最后一班车开出的时间一定在[t[i],t[i]+m] 之间,因为最差上一班车会在t[i]1 开出,t[i]+m1回来,如果我们让车停在那里再开出显然会让乘客白等。

因此我们就可以改变f[i][j] 为前ii 个人全上车或到站,最后一班车从t[i]+jt[i]+j 时开出的最小等待时间。

状态转移方程可以很容易的写出:

f[i][j]=min(f[i][j],f[k][l]+sum(k+1,i,t[i]+j))(你得保证变量合法)

最终答案就是min(f[n][0]f[n][m1])

其中sum(i,j,k) 函数表示第i 人到第j人等时刻k 发车的等待时间和,显然预处理前缀和就可以O(1) 算了。

但是这个式子也是O(n2m2)过不了怎么办?

我们jj 的下限肯定是max(t[k]+l+mt[i],0) 的,但是真的有必要从这个下限枚举到m1 吗?

显然,到i 上车时,比起让车等着,不如能早发就早发,所以虽然下限往上的状态f 可能会错,但答案一定不会错。

因此没必要枚举j ,复杂度O(n2m)

最后,放上代码

没有,还请大家

感谢大家的支持

#include<bits/stdc++.h> //万能的头文件
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
int n,m,t[505],mem[505][505];
int solve(int i,int st)
{
    if (i==n+1)
        return 0;
    if (st<t[i]) 
        return solve(i,t[i]);
    if (mem[i][st-t[i]])//记忆化 
        return mem[i][st-t[i]];
    int sum=0,j=i;
    while (j<=n && t[j]<=st)
        sum+=t[j++];
    int best=st*(j-i)-sum+solve(j,st+m); 
    for (;j<=n;j++)
    {
        sum+=t[j];
        best=min(t[j]*(j-i+1)-sum+solve(j+1,t[j]+m),best);
    }
    return mem[i][st-t[i]]=best;
}
int main()
{
    n=read(),m=read();
        t[i]=read();
    sort(t+1,t+n+1);
    cout << solve(1,0) << endl;
    return 0;
}

 

小蒟蒻ΩωΩ I AK IOI.
原文地址:https://www.cnblogs.com/20070618hyz/p/12728814.html