题目描述

上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂。这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭。由于每个人的口味(以及胃口)不同,所以他们要吃的菜各有不同,打饭所要花费的时间是因人而异的。另外每个人吃饭的速度也不尽相同,所以吃饭花费的时间也是可能有所不同的。

THU ACM小组的吃饭计划是这样的:先把所有的人分成两队,并安排好每队中各人的排列顺序,然后一号队伍到一号窗口去排队打饭,二号队伍到二号窗口去排队打饭。每个人打完饭后立刻开始吃,所有人都吃完饭后立刻集合去六教地下室进行下午的训练。

现在给定了每个人的打饭时间和吃饭时间,要求安排一种最佳的分队和排队方案使得所有人都吃完饭的时间尽量早。

假设THU ACM小组在时刻0到达十食堂,而且食堂里面没有其他吃饭的同学(只有打饭的师傅)。每个人必须而且只能被分在一个队伍里。两个窗口是并行操作互不影响的,而且每个人打饭的时间是和窗口无关的,打完饭之后立刻就开始吃饭,中间没有延迟。

现在给定N个人各自的打饭时间和吃饭时间,要求输出最佳方案下所有人吃完饭的时刻。

解析

真的难,看了题解发现自己思路没问题,状态却迟迟想不出来,状态转移也只是yy出来个大概。


首先想到要对序列排序,容易想到吃饭慢而打饭快的人对后面的人造成的影响是最小的,因此我们把这种人排在前面。此时就可以以打饭打到某个人为阶段做dp。

显然我们在转移时要同时考虑两个队列的用时情况,但是把什么写在状态里,把什么存起来又是个大问题,反正我是在这里懵圈了。最开始我思考了一个\(dp[0/1][0/1][i]\),想着把每一队最早打饭时间和最早吃完时间都存起来,于是发现没法转移。

由于题目要求吃完饭的时间,我们理所当然地要把从\(1\sim i\)个人的吃完饭的总时间存进dp数组,然而用什么作状态又是个大问题。然后我就寻思着\(dp[i][h][k]\)存最早吃完饭的时间,\(h,k\)分别表示每一队打完饭的时间行不?然而这样会爆空间。

无奈,看了看题解。

以上是瞎bb,以下是正解。


设\(dp[i][j]\)表示前\(i\)个人在某一队打了\(j\)时间的饭时,最早吃完的时间。此时我们维护一个打饭时间的前缀和,那么另一队的打饭时间也就出来了,那么转移也就很好想了。

情况一:

转移时,我们要考虑把新的一个人加入两个队列的其中一个,使得整体吃饭时间最少。那么显然,如果这个人加入某一队,其打完饭并吃完饭之后他前面的人还没吃完这种情况是最优的,因为没有对整体吃饭时间造成任何影响

情况二:

其次若这个人加入任意一队都不会出现上述情况,即无论如何他加进哪一队,整体吃饭时间都会变多,那我们退而求其次,找出这个人造成的影响最小的一队,然后把他加进去。影响最小即,使整体吃饭时间增加得最少

那么就很容易写出转移方程了。

设\(a[i].a\)表示\(i\)的打饭时间,\(a[i].b\)表示\(i\)的吃饭时间,\(sum[i]\)表示打饭时间的前缀和。

\[dp[i][j]=\min(dp[i][j],\max(dp[i-1][j-a[i].a],j+a[i].b))j>=a[i].a
\]

\[dp[i][j]=\min(dp[i][j],\max(dp[i-1][j],sum[i]-j+a[i].b))
\]

解释一下,其中\(sum[i]-j\)就是另一队的打饭时间,取max是为了考虑上面讲到的两种情况。

看起来有点像背包,实际上不是严格意义上的背包啦。一定要说的话,可以理解成把每个人造成的“影响”放入背包,使得这个影响最小,就能使得整体吃饭时间最少(我口胡的,如果有错望dalao指出)。

代码很短,却蕴含了较大的思维量,也可以说是dp的一贯尿性吧(摊。

参考代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#define N 201
#define INF 0x7fffffff
using namespace std;
struct dat{
int a,b;
}a[N];
int dp[N][N*N],sum[N],n;
inline bool cmp(dat a,dat b)
{
if(a.b==b.b) return a.a<b.a;
return a.b>b.b;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d%d",&a[i].a,&a[i].b);
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;++i) sum[i]=sum[i-1]+a[i].a;
memset(dp,0x3f,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=n;++i)
for(int j=sum[i];j>=0;--j){
if(j>=a[i].a) dp[i][j]=min(dp[i][j],max(dp[i-1][j-a[i].a],j+a[i].b));
dp[i][j]=min(dp[i][j],max(dp[i-1][j],sum[i]-j+a[i].b));
}
int ans=INF;
for(int i=0;i<=sum[n];++i) ans=min(dp[n][i],ans);
cout<<ans<<endl;
return 0;
}

其实如果数据更严格,\(N^3\)应该也是过不了的,题解有大佬把它压缩了一下,然鹅我。。。我没看懂(哭。

P2577 [ZJOI2005]午餐[DP]的更多相关文章

  1. Luogu P2577 [ZJOI2005]午餐(dp)

    P2577 [ZJOI2005]午餐 题面 题目描述 上午的训练结束了, \(THU \ ACM\) 小组集体去吃午餐,他们一行 \(N\) 人来到了著名的十食堂.这里有两个打饭的窗口,每个窗口同一时 ...

  2. 洛谷P2577 [ZJOI2005]午餐 dp

    正解:序列dp 解题报告: 传送门! 这题首先要想到一个显然的贪心:每个窗口的排队顺序都是按照吃饭时间从大到小排序的 证明如下: 这种贪心通常都是用微扰法,这题也不例外 现在假如已经确定了每个窗口有哪 ...

  3. 洛谷P2577 [ZJOI2005]午餐 打饭时间作为容量DP

    P2577 [ZJOI2005]午餐 )逼着自己做DP 题意: 有n个人打饭,每个人都有打饭时间和吃饭时间.有两个打饭窗口,问如何安排可以使得总用时最少. 思路: 1)可以发现吃饭时间最长的要先打饭. ...

  4. [洛谷P2577] [ZJOI2005]午餐

    洛谷题目链接:[ZJOI2005]午餐 题目描述 上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂.这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭.由于每个人的 ...

  5. [ZJOI2005]午餐 (DP)

    [ZJOI2005]午餐 题目描述 上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂.这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭.由于每个人的口味(以及胃口 ...

  6. P2577 [ZJOI2005]午餐 状压DP

    题目描述 上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂.这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭.由于每个人的口味(以及胃口)不同,所以他们要吃的菜各 ...

  7. 【题解】洛谷P2577 [ZJOI2005] 午餐(DP+贪心)

    次元传送门:洛谷P2577 思路 首先贪心是必须的 我们能感性地理解出吃饭慢的必须先吃饭(结合一下生活) 因此我们可以先按吃饭时间从大到小排序 然后就能自然地想到用f[i][j][k]表示前i个人在第 ...

  8. P2577 [ZJOI2005]午餐

    题目描述 上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂.这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭.由于每个人的口味(以及胃口)不同,所以他们要吃的菜各 ...

  9. Luogu P2577 [ZJOI2005]午餐

    一道贪心+类背包DP的好题 首先发现一个十分显然的性质,没有这个性质整道题目都难以下手: 无论两队的顺序如何,总是让吃饭慢的人先排队 这是一个很显然的贪心,因为如果让吃饭慢的排在后面要更多的时间至少没 ...

随机推荐

  1. 博客迁移到github了

    博客迁移到github了,这边基本不更新, 主要是没有找到快捷的同步方法,手动同步太麻烦了,如果你有快速把github博客同步到博客园的方法请一定告诉我

  2. Oracle ROWNUM的陷阱

    先抛出一个问题: 我有一张表T,现在我想对表中1/4的记录作UPDATE操作,我的SQL如下: Update t set col1='123' where mod(rownum,4)=1 我能够得到想 ...

  3. SpringBoot系列教程web篇之Thymeleaf环境搭建

    上一篇博文介绍了如何使用Freemaker引擎搭建web项目,这一篇我们则看一下另外一个常见的页面渲染引擎Thymeleaf如何搭建一个web项目 推荐结合Freemaker博文一起查看,效果更佳 1 ...

  4. Python Tkinter 之Listbox控件

    Listbox为列表框控件,它可以包含一个或多个文本项(text item),可以设置为单选或多选.使用方式为Listbox(root,option...). 常用的参数列表如下: 一些常用的函数:

  5. Docker部署ELK 7.0.1集群之Kibana安装介绍

    1.下载镜像 [root@vanje-dev01 ~]# docker pull kibana: 2.安装部署 2.1 创建宿主机映射目录 [root@vanje-dev01 ~]# mkdir /e ...

  6. fwrite & fread 的使用

    每一次切换文件操作模式必须调用fclose关闭文件. 如果直接切换操作模式,文件将损坏(出现乱码)或操作失败. 在调用了fclose时,作为参数的文件指针将被回收,必须再次定义,因此最好将功能封装. ...

  7. Word样式教程

    目录 写在前面 样式可以解决什么问题? 本文适合于 快速入门 一切皆样式 样式与格式的关系 如何修改样式 建立新的样式 样式的匹配和更新 根据样式更新所选段落 根据所选段落更新样式 小结 进一步了解 ...

  8. [高清·非影印] 深度学习入门:基于Python的理论与实现 + 源代码

    ------ 郑重声明 --------- 资源来自网络,纯粹共享交流, 如果喜欢,请您务必支持正版!! --------------------------------------------- 下 ...

  9. 单实例dg软件从10.2.0.4版本安装至10.2.0.5.12

    DG环境搭建需求,因此安装与主库相同的软件版本 1.主库软件版本10.2.0.5.12 2dg环境提供的是全新的10.2.0.4.0 3.安装步骤,安装10.2.0.5 静默安装 psu安装10.2. ...

  10. SpringBoot打成jar包后无法读取resources资源文件

    在项目中做了一个支付功能, 需要引入第三方渠道的配置文件config.xml用来初始化文件证书, 将配置文件 config.xml 放到 resources 资源目录下. 本地开发环境下能正常读取该文 ...