题目描述

上午的训练结束了,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. Jdk14 都要出了,Jdk9 的新特性还不了解一下?

    Java 9 中最大的亮点是 Java 平台模块化的引入,以及模块化 JDK.但是 Java 9 还有很多其他新功能,这篇文字会将重点介绍开发人员特别感兴趣的几种功能. 这篇文章也是 Java 新特性 ...

  2. 02-Redis

    今日内容 1. redis 1. 概念 2. 下载安装 3. 命令操作 1. 数据结构 4. 持久化操作 5. 使用Java客户端操作redis Redis 1. 概念:redis是一款高性能的NOS ...

  3. Digital Twin 数字孪生

    GE的一个NB视频:http://v.youku.com/v_show/id_XMjk0NTMzODIyNA==.html http://www.gongkong.com/news/201701/35 ...

  4. 2,简单的Python爬虫

    前言 根据上一篇 1,Python爬虫环境的安装我们已经在本地安装好了Python环境,那么这一篇就开始学习如何用Python来爬虫! 环境:操作系统:Windows10 IDE:   PyCharm ...

  5. springcloud 项目源码 微服务 分布式 Activiti6 工作流 vue.js html 跨域 前后分离

    1.代码生成器: [正反双向](单表.主表.明细表.树形表,快速开发利器)freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本.处理类.service等完整模块2. ...

  6. PHP0024:PHP 博客项目开发

  7. C++中字符常量与字符常量不能直接相加

    定义string变量,并进行初始化,如下: string s1 = "Hello"; string s2 = s1 + "World"; string s3 = ...

  8. if 语句 总结笔记

    1.if 语句 语法: if(condition) statement1; else statement2; graph TD A[JAVA考试] -->|几天后| B(收到成绩单) B --& ...

  9. nodejs爬虫--抓取CSDN某用户全部文章

    最近正在学习node.js,就像搞一些东西来玩玩,于是这个简单的爬虫就诞生了. 准备工作 node.js爬虫肯定要先安装node.js环境 创建一个文件夹 在该文件夹打开命令行,执行npm init初 ...

  10. 小白的linux笔记4:几种共享文件方式的速度测试——SFTP(SSH)/FTP/SMB

    测试一下各个协议的速度,用一个7205M的centos的ISO文件上传下载.5Gwifi连接时,本地SSD(Y7000)对服务器的HDD: smb download 23M/s(资源管理器) smb ...