1. 程式人生 > >2018普及組擺渡車洛谷5017(dp做法)

2018普及組擺渡車洛谷5017(dp做法)

-c 博客 不變 csdn 和數 log 數組 一點 include

啦啦啦,這一篇是接上一篇的博客,上一篇是記憶化搜索,而這一篇是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++)

2018普及組擺渡車洛谷5017(dp做法)