P2577 [ZJOI2005]午餐[DP]
题目描述
上午的训练结束了,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]\)表示打饭时间的前缀和。
\]
\]
解释一下,其中\(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]的更多相关文章
- Luogu P2577 [ZJOI2005]午餐(dp)
P2577 [ZJOI2005]午餐 题面 题目描述 上午的训练结束了, \(THU \ ACM\) 小组集体去吃午餐,他们一行 \(N\) 人来到了著名的十食堂.这里有两个打饭的窗口,每个窗口同一时 ...
- 洛谷P2577 [ZJOI2005]午餐 dp
正解:序列dp 解题报告: 传送门! 这题首先要想到一个显然的贪心:每个窗口的排队顺序都是按照吃饭时间从大到小排序的 证明如下: 这种贪心通常都是用微扰法,这题也不例外 现在假如已经确定了每个窗口有哪 ...
- 洛谷P2577 [ZJOI2005]午餐 打饭时间作为容量DP
P2577 [ZJOI2005]午餐 )逼着自己做DP 题意: 有n个人打饭,每个人都有打饭时间和吃饭时间.有两个打饭窗口,问如何安排可以使得总用时最少. 思路: 1)可以发现吃饭时间最长的要先打饭. ...
- [洛谷P2577] [ZJOI2005]午餐
洛谷题目链接:[ZJOI2005]午餐 题目描述 上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂.这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭.由于每个人的 ...
- [ZJOI2005]午餐 (DP)
[ZJOI2005]午餐 题目描述 上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂.这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭.由于每个人的口味(以及胃口 ...
- P2577 [ZJOI2005]午餐 状压DP
题目描述 上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂.这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭.由于每个人的口味(以及胃口)不同,所以他们要吃的菜各 ...
- 【题解】洛谷P2577 [ZJOI2005] 午餐(DP+贪心)
次元传送门:洛谷P2577 思路 首先贪心是必须的 我们能感性地理解出吃饭慢的必须先吃饭(结合一下生活) 因此我们可以先按吃饭时间从大到小排序 然后就能自然地想到用f[i][j][k]表示前i个人在第 ...
- P2577 [ZJOI2005]午餐
题目描述 上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂.这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭.由于每个人的口味(以及胃口)不同,所以他们要吃的菜各 ...
- Luogu P2577 [ZJOI2005]午餐
一道贪心+类背包DP的好题 首先发现一个十分显然的性质,没有这个性质整道题目都难以下手: 无论两队的顺序如何,总是让吃饭慢的人先排队 这是一个很显然的贪心,因为如果让吃饭慢的排在后面要更多的时间至少没 ...
随机推荐
- 【Linux】Linux下进程间的通信方式
本文内容: 1.进程通信的目的 2.介绍Linux下进程间的4种通信方式:管道,消息队列,共享内存,信号量 ps:套接字也可以用于进程间的通信,不过是不同物理机器上的进程通信,本章讨论是是同一台物理机 ...
- nginx实现tcp负载均衡
1 安装支持库 yum -y install make zlib zlib-devel gcc-c++ libtool openssl openssl-devel yum install pcre-d ...
- 在Linux下,使用Xmanager图形化显示
linux在安装Oracle时,需要图形化展示,本博客将使用xclock显示钟表进行测试 首先需要下载Xmanager网址:http://www.xshellcn.com/ 安装之后 打开之后,点击t ...
- 【转帖】国产PCIe SSD主控芯片获得中国芯大奖 3500MB/s读取
国产PCIe SSD主控芯片获得中国芯大奖 3500MB/s读取 https://www.cnbeta.com/articles/tech/906033.htm 国产主控 在日前的2019“中国芯”集 ...
- JVM——java内存模型和线程
概述 计算机的运算速度与它的存储和通信子系统速度的差距太大,大量的时间都花费在磁盘I/O.网络通信或者数据库访问上.我们当然不希望处理器大部分时间都处于等待其他资源的状态,要通过一些“手段”去把处理器 ...
- python基础 — Mysql Server
sql server对于字符类型的有:char:固定长度,存储ANSI字符,不足的补英文半角空格.nchar:固定长度,存储Unicode字符,不足的补英文半角空格varchar:可变长度,存储ANS ...
- 44 容器(三)——ArrayList索引相关方法
方法都比较简单,这里列出来即可: add(index,ele) //忘制定下标插入元素 add(ele) addAll(Collection <C> c) 泛型必须与调用add的泛型保持一 ...
- JAVA day2 语言基础
一.注释 描述代码的文字 1.// 单行注释 2./* */ 多行注释 (多行注释中不能再嵌套多行注释) 3./** */ 多行注释 配合JavaDoc工具使用(只可以看到注释,看不到代码 ...
- jwt 0.9.0(三)jwt客户端存储状态可行性分析,及Java代码案例
Jwt客户端存储状态可行性分析 1.前端首次访问后台,后台生成token,放在http header的Authorization里(官网推荐,可解决跨域cookie跨域问题),并且Authorizat ...
- Microsoft.AspNet.Identity 自定义使用现有的表—登录实现,aspnet.identity
Microsoft.AspNet.Identity是微软新引入的一种membership框架,也是微软Owin标准的一个实现.Microsoft.AspNet.Identity.EntityFrame ...