P5017 [NOIP2018 普及组] 摆渡车 题解
P5017 [NOIP2018 普及组] 摆渡车
题目 [NOIP2018 普及组] 摆渡车
题目描述
有
凯凯很好奇,如果他能任意安排摆渡车出发的时间,那么这些同学的等车时间之和最小为多少呢?
注意:摆渡车回到人大附中后可以即刻出发。
输入格式
第一行包含两个正整数
第二行包含
输出格式
输出一行,一个整数,表示所有同学等车时间之和的最小值(单位:分钟)。
样例 #1
样例输入 #1
5 1
3 4 4 3 5
样例输出 #1
0
【数据规模与约定】
对于
对于
对于
另有
对于
思路-1
将实际问题抽象后,不难发现这是一个 区间
我们不妨认为时间是一条数轴,每名同学按照到达时刻分别对应数轴上可能重合的点。安排车辆的工作,等同于将数轴分成若干个左开右闭段,每段的长度
转移:
但是这样显然时间复杂度会超标
考虑使用前缀和优化掉那个大大的
之后,转移式可以这样写:
这里令
此时考虑时间复杂度:非常不合理
考虑优化
仍然考虑
此时再来考虑时间复杂度:
再考虑优化
假设正在求
然而直接扔掉这个状态,会与上一个优化缩小转移范围起冲突,故无用的位置令
此时的时间复杂度就已经非常优秀了:
CPP-1
#include <bits/stdc++.h>
using namespace std;
const int N=4e6+10;
const int INF=1e9;
int n,m,T;
int a[N],f[N],s[N];
inline int max(int a,int b) {
return a>b?a:b;
}
inline int min(int a,int b) {
return a<b?a:b;
}
inline int read() {
int x, f = 1;
char c;
while (!((c = getchar()) >= '0' && c <= '9')) if (c == '-') f = -1;
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9') (x *= 10) += c - '0';
return x * f;
}
inline void write(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10);
putchar(x % 10 ^ 48);
}
int main() {
n=read(),m=read();
for(int i=1; i<=n; i++) {
int t=read();
a[t]++;
s[t]+=t;
T=max(T,t);
}
for(int i=1; i<m+T; i++) {
a[i]+=a[i-1];
s[i]+=s[i-1];
}
for(int i=0; i<m+T; i++) {
if(i>=m && a[i-m]==a[i]) {
f[i]=f[i-m];
continue;
}
f[i]=a[i]*i-s[i];
for(int j=max(0,i-(m<<1)+1); j<=i-m; j++)
f[i]=min(f[i],f[j]+(a[i]-a[j])*i-(s[i]-s[j]));
}
int ans=INF;
for(int i=T; i<T+m; i++)
ans=min(ans,f[i]);
write(ans);
putchar('\n');
return 0;
}
思路-2
我们来考虑递推式:
则有:
继续推导,则有:
不妨令
这很显然就是一个斜率优化的式子了:
而在DP的设置中,我们要求:
那么我们可以开始写代码了
需要注意的是:在斜率优化中我们难免会遇到求斜率的地方,而这种情况下程序无法避免的误差,可以通过二者相乘解决,详细可见我对P5785那道题的注释
CPP-2
#include <bits/stdc++.h>
using namespace std;
const int N=4e6+10;
int n,m,t,ti,ans=1e9;
int sum[N],cnt[N];
int q[N],f[N];
int l=1,r=0;
inline int read() {
int x, f = 1;
char c;
while (!((c = getchar()) >= '0' && c <= '9')) if (c == '-') f = -1;
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9') (x *= 10) += c - '0';
return x * f;
}
inline double slope(int x,int y){
return (f[y]+sum[y]-f[x]-sum[x])/(double)(cnt[y]==cnt[x]?1e-9:cnt[y]-cnt[x]);
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
ti=read();
t=max(t,ti);
cnt[ti]++;
sum[ti]+=ti;
}
for(int i=1;i<t+m;i++){
cnt[i]+=cnt[i-1];
sum[i]+=sum[i-1];
}
for(int i=0;i<t+m;i++){
if(i-m>=0){ // j = i - m
while(l<r && slope(q[r-1],q[r])>=slope(q[r],i-m)) --r;
q[++r]=i-m;
}
while(l<r && slope(q[l],q[l+1])<=i) ++l;
f[i]=i*cnt[i]-sum[i];
if(l<=r) f[i]=min(f[i],f[q[l]]+(cnt[i]-cnt[q[l]])*i-(sum[i]-sum[q[l]]));
}
for(int i=t;i<t+m;i++) ans=min(f[i],ans);
printf("%d\n",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】