题目传送门

题目描述

有 n 名同学要乘坐摆渡车从人大附中前往人民大学,第 i 位同学在第 ti 分钟去 等车。只有一辆摆渡车在工作,但摆渡车容量可以视为无限大。摆渡车从人大附中出发、 把车上的同学送到人民大学、再回到人大附中(去接其他同学),这样往返一趟总共花费 $m$ 分钟(同学上下车时间忽略不计)。摆渡车要将所有同学都送到人民大学。

凯凯很好奇,如果他能任意安排摆渡车出发的时间,那么这些同学的等车时间之和最小为多少呢?

注意:摆渡车回到人大附中后可以即刻出发。

输入格式

第一行包含两个正整数 $n, m$,以一个空格分开,分别代表等车人数和摆渡车往返一趟的时间。    
第二行包含 $n$ 个正整数,相邻两数之间以一个空格分隔,第 $i$ 个非负整数 $t_i$ 代表第 $i$ 个同学到达车站的时刻。

输出格式

输出一行,一个整数,表示所有同学等车时间之和的最小值(单位:分钟)。

样例 #1

样例输入 #1
5 1 
3 4 4 3 5

样例输出 #1
0

样例 #2

样例输入 #2
5 5 
11 13 1 5 5

样例输出 #2
4

提示

【输入输出样例 1 说明】

同学 $1$ 和同学 $4$ 在第 $3$ 分钟开始等车,等待 $0$ 分钟,在第 $3$ 分钟乘坐摆渡车出发。摆渡车在第 $4$ 分钟回到人大附中。   
同学 $2$ 和同学 $3$ 在第 $4$ 分钟开始等车,等待 $0$ 分钟,在第 $4$ 分钟乘坐摆渡车 出发。摆渡车在第 $5$ 分钟回到人大附中。   
同学 $5$ 在第 $5$ 分钟开始等车,等待 $0$ 分钟,在第 $5$ 分钟乘坐摆渡车出发。自此 所有同学都被送到人民大学。总等待时间为 $0$。

【输入输出样例 2 说明】

同学 $3$ 在第 $1$ 分钟开始等车,等待 $0$ 分钟,在第 $1$ 分钟乘坐摆渡车出发。摆渡 车在第 $6$ 分钟回到人大附中。   
同学 $4$ 和同学 $5$ 在第 $5$ 分钟开始等车,等待 $1$ 分钟,在第 $6$ 分钟乘坐摆渡车 出发。摆渡车在第 $11$ 分钟回到人大附中。   
同学 $1$ 在第 $11$ 分钟开始等车,等待 $2$ 分钟;同学 $2$ 在第 $13$ 分钟开始等车, 等待 $0$ 分钟。他/她们在第 $13$ 分钟乘坐摆渡车出发。自此所有同学都被送到人民大学。 总等待时间为 $4$。  
可以证明,没有总等待时间小于 $4$ 的方案。

【数据规模与约定】    
对于 $10\%$ 的数据,$n ≤ 10, m = 1, 0 ≤ t_i ≤ 100$。   
对于 $30\%$ 的数据,$n ≤ 20, m ≤ 2, 0 ≤ t_i ≤ 100$。  
对于 $50\%$ 的数据,$n ≤ 500, m ≤ 100, 0 ≤ t_i ≤ 10^4$。  
另有 $20\%$ 的数据,$n ≤ 500, m ≤ 10, 0 ≤ t_i ≤ 4 \times  10^6$。  
对于 $100\%$ 的数据,$n ≤ 500, m ≤ 100, 0 ≤ t_i ≤ 4 \times 10^6$。

一,记忆化搜索

具体内容见代码注释

#include <bits/stdc++.h>using namespace std;inline 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];//因为0<=st-t[i]<=m,因此可以记忆化,把这个作为状态的第二维 int solve(int i,int st)//记忆化搜索。i:第i个人,st:开始时间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()
{//memset(mem,-1,sizeof(mem));n=read(),m=read();for (int i=1;i<=n;i++)t[i]=read();sort(t+1,t+n+1);//显然从小到大按照时间排序更好算 cout << solve(1,0) << endl;return 0;
}

二,DP

首先,看完题目意思,就想到了算法——DP

那么如何定义状态呢?首先,排序是必然的。

接着,最容易想到的,最简单的状态定义:

用 f[i] 表示前 i 个人都已经到达B地(上车也可以算到达,毕竟不用等了),所需要的最少总等候时间

状态怎么转移?这个题我们选择刷表

枚举 k,f[k] = f[i] + 第 (i+1)个学生到第 k 个学生的总等车时间。

这个总等车时间就是x=i+1∑k​(T−t[x])。T 是车重新到达A地的时间。

我们可以把上一次车到达的时间记录下来,放到DP状态的一维里去。这样状态定义就变成了:

用 f[i][j] 表示前 i 个人在 j 时刻都已经到达B地(同上),所需要的最少总等候时间。

此时 j 就是前 i 个人最后全部接走时车到达的时间。

重新观察状态转移方程,其中 max(j+m,t[k]) 就是 T。看似好像没有什么问题了。

但是这样一来,j 又是 ti​ 这个级别的了,承受不起,怎么办呢?

注意到此时,j⩾t[i]+m 的情况都是无意义的,因为如果j⩾t[i]+m,则说明第 i 个人等了  ⩾m 分钟。但是即使车在 t[i]−1 时刻开走,他也只会等上 m − 1 分钟啊!(注意,此时我们讨论的是车一到就把第 i 个人接走的情况,不考虑第 i+1 个人以及之后的人,如有不解见状态定义)

所以离散化状态定义变成了:

用 f[i][j] 表示在第 i 个人等了刚好 j 分钟的时刻,前 i 个人已经到达B地(同上),所需要的最少总等候时间。

此时前 i 个人最后全部接走时车到达的时间就是 t[i]+j。

所以我们可以算出状态转移方程中应该添加的那一维(f[k]→f[k][?] 中的 ?),也就是第 k 个人的等待时间是:

max( t[i] + j + m - t[k], 0 ) = tmp(下文因为这个量多次出现,所以简化为 tmp)

t[i]+j+m 就是车到的时间。减去 t[k] 就是等候的时间。

得出:T= 第 k 个人等候的时间 +t[k]=tmp+t[k]

所以现在状态转移方程长这样:

f[k][tmp] = f[i][j] +f[k][tmp]=f[i][j]+ 第 (i+1)个学生到第 k 个学生的总等车时间。

总等车时间 =x=i+1∑k​(T−t[x])。T = tmp + t[k]。

f[k][tmp]=f[i][j]+x=i+1∑k​(tmp+t[k]−t[x])

计算一下时间复杂度(最差):遍历状态 O(nm),枚举转移 O(n),转移过程。。。怎么还要 O(n)?!O(n^3m) 不是炸了吗qwq

仔细观察那个∑,发现 (tmp + t[k]) 是定值,所以提出来:

f[k][tmp]=f[i][j]+(tmp+t[k])×(k−i)−x=i+1∑k​t[x]

然后那个 ∑ 珂以前缀和优化!

f[k][tmp]=f[i][j]+(tmp+t[k])×(k−i)−(t[k]−t[i])

至此,转移复杂度O(1)。

所以代码就可以写出来啦~

注意:我写的代码里,枚举的 k 不是转移方程中的 k,转移方程中的 k 在我的代码中是 i + k(具体看了代码就明白了)。边界情况我直接把 t[0] 设做了 −inf(假设有第0个人,上车时间是 −inf,从0开始做,就可以不用更新f[i][0] 了)。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int t[501],s[501],f[501][101];
const int inf=2139062143;
inline int read()
{int neg=1,x=0;char c;while((c=getchar())<'0'||c>'9')if(c=='-')neg=-1;x=c-'0';while((c=getchar())>='0'&&c<='9')x=x*10+(c-'0');return x*neg;
}
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];memset(f,0x7f,sizeof(f));t[0]=-inf;f[0][0]=0;for(int i=0;i<=n;i++){int MAX=min(m-1,t[i+1]-t[i]);for(int j=0;j<=MAX;j++)if(f[i][j]!=inf)for(int k=1;i+k<=n;k++){int tmp=max(t[i]+j+m-t[i+k],0);f[i+k][tmp]=min(f[i+k][tmp],f[i][j]+(tmp+t[i+k])*k-(s[i+k]-s[i]));}}int ans=inf;for(int i=0;i<m;i++)ans=min(ans,f[n][i]);printf("%d\n",ans);return 0;
}

P5017 [NOIP2018 普及组] 摆渡车 题解相关推荐

  1. P5017 NOIP2018 普及组 摆渡车

    P5017 NOIP2018 普及组 摆渡车 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 显然要把人按照到达时间排序.然后考虑 dp. 设 \(f(i)\) 表示前 \(i\) ...

  2. P5017 [NOIP2018 普及组] 摆渡车(pj组最后一道蓝) P1027 [NOIP2001 提高组] Car 的旅行路线(提高组第一道蓝)

    先从简单的说 确实 我是看着AC率高的才选的 没有辜负我的期望 他真的很简单 数据范围很小 一眼floyd 然后就秒了 注意得就是我们还是把每个点编号再跑最短路 然后我们可以把相邻的四个放一起处理 还 ...

  3. [NOIP2018 普及组] 摆渡车题解

    题目链接 题意 给出n名同学到达车站的时间,只有一辆摆渡车,来回需要mmm分钟,求将所有同学摆渡到终点,所有同学的最少等车时间之和. 抽象一下题意: 数轴:时间轴 点权值:此时刻等车的人数 区间右端点 ...

  4. NOIp2018普及组-摆渡车

    (作为一名已经离开了普及组的退役选手感到万分庆幸) 题目链接:https://www.luogu.org/problemnew/solution/P5017 1.感觉就是一个类似线性的以时间作为维度的 ...

  5. NOIP2018普及组初赛题解

    展开全文 第24届全国青少年信息学奥林匹克联赛初赛 普及组C++语言试题 竞赛时间:2018 年 10 月 13 日 14:30~16:30 选手注意: 1.试题纸共有 7 页,答题纸共有 2 页,满 ...

  6. 信息学奥赛一本通 1981:【18NOIP普及组】对称二叉树 | 洛谷 P5018【NOIP2018 普及组】 对称二叉树

    [题目链接] ybt 1981:[18NOIP普及组]对称二叉树 洛谷 P5018[NOIP2018 普及组] 对称二叉树 [题目考点] 二叉树 [解题思路] 先求出二叉树中各子树的结点数 遍历二叉树 ...

  7. 信息学奥赛一本通 1979:【18NOIP普及组】龙虎斗 | 洛谷 P5016 [NOIP2018 普及组] 龙虎斗

    [题目链接] ybt 1979: [18NOIP普及组]龙虎斗 洛谷 P5016 [NOIP2018 普及组] 龙虎斗 [题目考点] 1. long long类型使用 已知变量a, b是int类型的变 ...

  8. 信息学奥赛一本通 1978:【18NOIP普及组】标题统计 | 洛谷 P5015 [NOIP2018 普及组] 标题统计

    [题目链接] ybt 1978:[18NOIP普及组]标题统计 洛谷 P5015 [NOIP2018 普及组] 标题统计 [题目考点] 1. 字符串 读入带空格的字符串 将带空格的字符串读入字符数组 ...

  9. P1008 [NOIP1998 普及组] 三连击 题解

    P1008 [NOIP1998 普及组] 三连击 题解 题目背景 本题为提交答案题,您可以写程序或手算在本机上算出答案后,直接提交答案文本,也可提交答案生成程序. 题目描述 将 1, 2, - , 9 ...

最新文章

  1. webpack打包HTML配置自动,十三、HtmlWebpackPlugin的使用 ------- 2019-04-25
  2. python3 nmap 函数简介
  3. 拒绝图片延迟加载,爽爽的看美图
  4. ECMAScript6 新特性——“字符串的扩展”
  5. .NET Core 在程序集中集成Razor视图
  6. 论文浅尝 | 基于知识图谱难度可控的多跳问题生成
  7. python 访问网站 json_python爬虫用selenium访问一个网址返回的是个json字符串,怎么获取这个json字符串?...
  8. 微软一站式示例代码库(中文版)2011-03-10版本, 新添加20个示例
  9. 开源大数据:Iceberg新一代数据湖技术实践
  10. layui结合zTree写树状图
  11. 《文言文复兴系列 5 人之道》(江湖一剑客)
  12. 《FireShot》一键滚动截屏整个网页
  13. 解决ubuntu无法解析域名问题
  14. LSTM(长短期记忆网络)原理与在脑电数据上的应用
  15. Spring XML 和 注解形式指定init 、destory方法
  16. 小程序支付报错:向微信请求统一下单失败:商户号该产品权限未开通,请前往商户平台>产品中心检查后重试
  17. 浙江移动面试二面(2009)
  18. Wdf框架之WdfObject状态机(3)-前篇
  19. 世平信息助力2018第三届SSC安全峰会
  20. 录像带/摄像带转光盘(上海)

热门文章

  1. word文件打开就是只读模式的解决方法
  2. 禁用计算机声卡设备,电脑没声音,提示没有音频设备怎么办
  3. android opencv单机版人脸识别+比对
  4. 远离负能量,才能获得正能量
  5. ElementUI - <el-image> src 属性使用本地图片加载失败解决方案
  6. 湖南工业大学c语言 期末考试程序改错,高考各科答题规范,90%的同学都不了解!现在改还来得及!...
  7. OpenCV - 最大间方差分割
  8. java循环引用讲解_Java循环引用
  9. 如何实现以上垂直方向上两个TextView内文字左右对齐
  10. 音频视频自动播放解决方案