洛谷5017:摆渡车——题解

https://www.luogu.org/problem/P5017

参考:https://www.luogu.org/blog/ztyluogucpp/solution-p5017

我想我大概是废了。

肯定是要对$t$排序的。

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

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

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

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

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

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

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

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

最终答案就是$min(f[n][0]\sim f[n][m-1])$

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

但是这个式子也是$O(n^2m^2)$过不了怎么办?

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

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

因此没必要枚举$j$,复杂度$O(n^2m)$

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=505;
const int M=105;
const ll INF=1e18;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
ll t[N],s[N],f[N][M];
ll sum(int l,int r,ll now){
    return now*(r-l+1)-s[r]+s[l-1];
}
int main(){
    int n=read(),m=read();
    for(int i=1;i<=n;i++)t[i]=read();
    sort(t+1,t+n+1);
    for(int i=1;i<=n;i++)s[i]=s[i-1]+t[i];
    for(int i=1;i<=n;i++){
        for(int j=0;j<m;j++)f[i][j]=sum(1,i,t[i]+j);
        for(int j=1;j<i;j++){
            for(int k=0;k<m;k++){
                ll now=max(t[j]+k+m,t[i]);
                f[i][now-t[i]]=min(f[i][now-t[i]],f[j][k]+sum(j+1,i,now));
            }
        }
    }
    ll ans=INF;
    for(int i=0;i<m;i++)ans=min(ans,f[n][i]);
    printf("%lld\n",ans);
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

 +本文作者:luyouqi233。               +

 +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

posted @ 2019-08-16 17:41  luyouqi233  阅读(302)  评论(0编辑  收藏  举报