2018普及组摆渡车洛谷5017(dp做法)

啦啦啦,这一篇是接上一篇的博客,上一篇是记忆化搜索,而这一篇是dp+前缀和小技巧

dp这种玄学做法我这种蒟蒻当然不是自己想出来的,参考https://blog.csdn.net/kkkksc03/article/details/84798228


 

首先定义f[i],表示在时间i已积累的最小等待时间,因为时间范围是<=4*10^6,所以不会炸。定义cnt[i]表示到i时刻时已经到达的人数(包括被车送走的),sum[i]表示到i时刻时到达车站的同学的时间的总和,然后就可推出转移方程:f[i]=min(f[i],f[j]+(cnt[i]-cnt[j])*i-(sum[i]-sum[j])。f[j]指的是上一次发车的时间。咳,那么这个方程是怎么推出来呢?


 

cnt[i]-cnt[j]指的是从i到j中新加入的新童鞋,在j到i这段时间中新加入的会积累等待时间,假设加入了s1,s2,s3(时间)三位童鞋,积累的等待时间即为i-s1+i-s2+i-s3,等于i*(cnt[i]-cnt[j])-(sum[i]-sum[j])

代码中的一点也是相同的思想:f[i]=cnt[i]*i-sum[i]

剩下就是代码啦,里面也有详细注释

#include<bits/stdc++.h>
using namespace std;
int a[501],cnt[4000005],sum[4000005],f[4000005];
//f[i]指i时刻已经积累的所有人的等待时间和 
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int Time=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        cnt[a[i]]++;//在a[i]时间到达的人 
        sum[a[i]]+=a[i];//sum是前缀和数组 
        Time=max(Time,a[i]);//求出到达时间中的最大,加上m作为dp的上限 
    }
    for(int i=1;i<Time+m;i++)
    {
        cnt[i]+=cnt[i-1];//cnt[i]指的是到i时刻时,已经到达了的人数(包括送走的) 
        sum[i]+=sum[i-1];//sum[i]代表第i时间到达车站的同学的时间的总和
    }
    for(int i=0;i<Time+m;i++)//dp时间 
    //Time+m:最后一个人最晚送的时间小于Time+m(也就是上一班车刚好在最后一个人前发车,刚好把最后一个人落下) 
    {
        if (i>=m&&cnt[i-m]==cnt[i])//到下一次发车时中途没有人来 
        {
             f[i]=f[i-m];//等待时间不变化,直接跳过即可 
             continue; 
        } 
        f[i]=cnt[i]*i-sum[i];//初始化,这样还没开第一班车 
        int tmp;
        tmp=max(i-2*m+1,0);//车的等待时间不会超过m,超过m还不如多开一次 
        for(int j=tmp;j<=i-m;j++)
          f[i]=min(f[i],f[j]+(cnt[i]-cnt[j])*i-(sum[i]-sum[j]));//转移方程 
    }
    int ans=2147483647;
    for(int i=Time;i<Time+m;i++)//注意范围 
      ans=min(ans,f[i]);
    printf("%d",ans);
    return 0;
}

呼!终于把这道题弄完了(rp++)

 

posted @ 2019-02-14 20:45  yyys  阅读(389)  评论(0编辑  收藏  举报