题目描述

上午的训练结束了,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. MySQL存储引擎——MyISAM与InnoDB区别

    注:本文来自:https://blog.csdn.net/xifeijian/article/details/20316775 InnoDB和MyISAM是许多人在使用MySQL时最常用的两个表类型, ...

  2. 快速幂——while理解&&[P1965] 转圈游戏

    快速幂--while理解 \[a^k\] 把k转成2进制 \[k=2^n*p[n]+2^(n-1)*p[n-1]+...+2^1*p[1]+2^0*p[0]\] \[a^k=a^(2^n*p[n]+2 ...

  3. oracle怎么建立本地连接

    sqlplus连接oracle数据库(连接本地oracle数据库和连接远程的oracle数据库) 虽然我们现在平时都是使用PLSQL Developer这个软件工具了,但是我们还是要了解sqlplus ...

  4. java jni 调用c语言函数

    今日在hibernate源代码中遇到了native关键词,甚是陌生,就查了点资料,对native是什么东西有了那么一点了解,并做一小记. native关键字说明其修饰的方法是一个原生态方法,方法对应的 ...

  5. 对特殊方法的访问 - Special method lookup

    对特殊方法的访问 - Special method lookup 对于用户自定义的 class 来说, 特殊方法只有通过定义对象的类型object’s type (而非通过 instance 的 __ ...

  6. php curl 检测网页是否被百度收录

    <?php /* * 检测百度是否收录网页 curl模式 * @ param string $url传入的url* return int (1 收录 0 不收录) */ function che ...

  7. Java8尽管很香,你想过升级到Java11吗?会踩那些坑?

    目前最新JDK 11,Oracle会一直维护到2026年. Java11的新特性 1.更新支持到Unicode 10编码 Unicode 10(version 10.0 of the Unicode ...

  8. CodeForces 327B 水题。

    I - 9 Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Statu ...

  9. js this是什么?[多次书写]

    前言 以前的时候,我写了一个关于js this的博客,写的非常复杂,分析了各种情况. 现在我想简化. 如果你有后台基础,专门去理解过this,那么请忘记. 这东西是有口诀的: 在方法中,this 表示 ...

  10. [PHP] 使用PHP在mongodb中进行count查询

    在php7的mongodb扩展中,当要查询某个集合在某个条件下的数据个数时,可以使用下面的方式来获取. 比原生的命令要复杂许多 比旧版mongo扩展也复杂许多 需要使用到MongoDB\Driver\ ...