题目描述

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

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

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

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

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

输入格式

第一行一个整数 \(N\) ,代表总共有 \(N\) 个人。

以下N行,每行两个整数 \(Ai\) ,\(Bi\) 。依次代表第 \(i\) 个人的打饭时间和吃饭时间。

输出格式

一个整数 \(T\) ,代表所有人吃完饭的最早时刻。

输入输出样例

输入 #1

5

2 2

7 7

1 3

6 4

8 5

输出 #1

17

说明/提示

所有输入数据均为不超过 \(200\) 的正整数。

————————————————————————————————————

这道题思维难度有点大,我是看了眼其他大佬的题解才会写的,在这里重新总结一下。

这道题看起来和经典的接水问题有相似之处,但是比接水问题多了“吃饭”这一过程以及另外的一个队列,要求我们制定出更加精妙的DP方程。

但是我们依然可以首先借鉴接水问题的贪心思路,即让吃饭速度慢的同学先打饭,最优性可以使用反证法证明。

于是我们获得了一个相对的打饭顺序(同时考虑两个队列),所以我们这样设立方程:

\(f[i][j][k]\) 表示前 \(i\) 个人在一号窗口打饭用时为 \(i\) ,在二号窗口打饭用时为 \(j\) 并且所有人都吃完饭的最短用时。

注意到这个方程的空间复杂度是 \(200^5\) 的,会MLE。

我们可以在排序之后用 \(sum\) 数组存一下前 \(i\) 个人打饭用时的前缀和,因为每人必然会在其中一个窗口打饭,所以可以用 %sum[i] - j% 算出在人们在二号窗口打饭的用时。

于是DP方程的第三维可以被优化掉,空间复杂度变成了 \(200^3\),可以接受。

如何进行状态转移?

首先考虑把当前的这个人放入一号队列的情况,

转移方程为:$ f[i][j]=min(f[i][j],max(f[i-1][j-p[i].a],j+p[i].b)) $

其中 \(max()\) 里面的两个元素分别表示当前的这个人打饭和吃饭速度都贼快,前面打饭的人还没吃完就已经打完饭并且吃完了当前的这个人吃完饭的时间是一号队列中最迟的(对此前的答案有贡献)两种情况。

由于每个变量值都是确定的,所以这两种情况要同时考虑。最后在这两值里面取最大值,再让 \(f[i][j]\) 取用时最短的大情况。

把当前人放入二号队列的情况同理,只不过是把 \(j\) 用 \(sum[i] - j\) 替换。

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

这样 \(f\) 数组就存了两个队列中所有人都吃完饭用时较长的一个。

代码如下:

#include <bits/stdc++.h>
#define FOR(i,s,t) for(int (i)=(s);(i)<=(t);(i)++)
#define MAXN 207
#define INF 0x3f3f3f3f
using namespace std;
struct Person { int a,b; }p[MAXN];
int n,ans=INF,sum[MAXN],f[MAXN][MAXN*MAXN];
inline bool cmp(const Person &A,const Person &B) { return A.b>B.b; }
inline int read() {
int w=0,X=0; char ch=0;
while (!isdigit(ch)) w|=ch=='-',ch=getchar();
while (isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
int main() {
memset(f,INF,sizeof(f));
memset(sum,0,sizeof(sum));
n=read();
FOR(i,1,n) p[i].a=read(),p[i].b=read();
sort(p+1,p+n+1,cmp);
FOR(i,1,n) sum[i]=sum[i-1]+p[i].a;
f[1][0]=f[1][p[1].a]=p[1].a+p[1].b;
for (int i=1;i<=n;i++) {
for (int j=1;j<=sum[i];j++) {
if (j>=p[i].a) f[i][j]=min(f[i][j],max(f[i-1][j-p[i].a],j+p[i].b));
f[i][j]=min(f[i][j],max(f[i-1][j],sum[i]-j+p[i].b));
if (i==n) ans=min(ans,f[i][j]);
}
}
printf("%d",ans);
return 0;
}

Luogu2577 | [ZJOI2005]午餐 (贪心+DP)的更多相关文章

  1. luogu2577 [ZJOI2005] 午餐 贪心

    题目大意 THU ACM小组的吃饭计划是这样的:先把所有的人分成两队,并安排好每队中各人的排列顺序,然后一号队伍到一号窗口去排队打饭,二号队伍到二号窗口去排队打饭.每个人打完饭后立刻开始吃,所有人都吃 ...

  2. luogu2577/bzoj1899 午餐 (贪心+dp)

    首先,应该尽量让吃饭慢的排在前面,先按这个排个序 然后再来决定每个人到底去哪边 设f[i][j]是做到了第i个人,然后1号窗口目前的总排队时间是j,目前的最大总时间 有这个i和j的话,再预处理出前i个 ...

  3. luogu 2577 [ZJOI2005]午餐 贪心+dp

    发现让 $b$ 更大的越靠前越优,然后依次决策将每个人分给哪个窗口. 令 $f[i][j]$ 表示考虑了前 $i$ 个人,且第一个窗口的总等待时间为 $j$ 的最小总时间. 然后转移一下就好了~ #i ...

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

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

  5. [ZJOI2005]午餐 (贪心,动态规划)

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

  6. 【BZOJ1899】[Zjoi2004]Lunch 午餐 贪心+DP

    [BZOJ1899][Zjoi2004]Lunch 午餐 Description 上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂.这里有两个打饭的窗口,每个窗口同一时 ...

  7. BZOJ 1899&&luogu P2577: [Zjoi2004]Lunch 午餐 贪心+DP

    贪它,再大力DP(话说觉得此题简单的真的是大佬QAQ)我想了两天...QWQ 贪心:吃饭慢的先打饭(不太会证...) DP:f[i][j]表示前i个人,在1号窗口打饭的总时间时j,的最短时间 确定i的 ...

  8. 洛谷 2577 [ZJOI2005]午餐——序列dp

    题目:https://www.luogu.org/problemnew/show/P2577 可以从只有一个窗口的角度思考出一个贪心结论.就是应当按吃饭时间(不算打饭时间)从大到小排序.这样交换相邻两 ...

  9. luogu2577 [ZJOI2005]午餐

    dp[i]表示第一队打饭时间i的最优解 #include <algorithm> #include <iostream> #include <cstring> #i ...

随机推荐

  1. 概念验证:在Kubernetes中部署ABAP

    对于将SAP ABAP应用服务器组件容器化和在Kubernetes中部署它们,我们在SPA LinuxLab中做了概念验证(PoC),本文将介绍一些我们的发现和经验.本文会也会指出这项工作的一些潜在的 ...

  2. 分布式SnowFlakeID(雪花ID)原理和改进优化

    最近在研究分布式框架的组件和整体设计思路.所有的问题,一旦涉及分布式难度就呈几何倍数的提升.包括最常见的ID生成也是,单机情况下,使用数据库自增ID.UUID都是简单易行的选择 但在分布式环境下,就需 ...

  3. Unity酱~ 卡通渲染技术分析(二)

    前面的话 上一篇Unity酱~ 卡通渲染技术分析(一) 写了CharaMain.cginc,服装的渲染是怎么实现的.这篇来分析一下头发跟皮肤的实现 头发 本来以为unitychan的头发会有各向异性的 ...

  4. [Effective Java 读书笔记] 第二章 创建和销毁对象 第三 四条

    第三条 用私有构造器或者枚举类型强化singleton属性 singleton指只能被实例化一次的类,即将构造器设置为私有,使用公有静态成员来实例化,且只实例化一次对象 第四条 通过私有构造器强化不可 ...

  5. PHP 中 new static 和 new self 的区别

            今天老大在公司 问了一下  new static  和 new self 的区别 公司十个程序 竟然没有一个回答上来 后面画面自补 ... 本屌丝回家后 就百度了解了下 这二者区别 : ...

  6. php连接mySql数据库 示例

    <?php header("content-type:text/html;charset=utf-8"); error_reporting(E_ALL); /* 连接数据库- ...

  7. java程序设计原则知多少

    程序设计七大原则 一.开闭原则 ​ 针对我们设计的功能模块对扩展开放,对修改关闭:利用面向接口(抽象)编程(多态的特性),实现对功能需求扩展的同时,不允许更改原来的代码.提高对象的可复用性.可维护性. ...

  8. 编写windows服务程序

    2012-11-02 08:54 (分类:计算机程序) windows服务是一个运行在后台并实现勿需用户交互的任务的控制台程序,对于隐藏程序有很大帮助. 用了几天时间概括了编写windows服务程序的 ...

  9. POST注入之sqlmap

    POST注入方法一加—form跑数据库sqlmap.py -u http://59.63.200.79:8815/Pass-05/index.php —form —dbs跑出数据库后查询表名 假设库名 ...

  10. SpringBoot整合NoSql--(四)Session共享

    简介: 正常情况下,HttpSession是通过Servlet 容器创建并进行管理的,创建成功之后都是保存在内存中.如果开发者需要对项目进行横向扩展搭建集群,那么可以利用一些硬件或者软件工具来做负载均 ...