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的好题 首先发现一个十分显然的性质,没有这个性质整道题目都难以下手: 无论两队的顺序如何,总是让吃饭慢的人先排队 这是一个很显然的贪心,因为如果让吃饭慢的排在后面要更多的时间至少没 ...
随机推荐
- .Net Core控制台应用程序使用依赖注入、配置文件等
.Net Core作为一门新语言,资料实在是太少了,并且国内学习的人也不多,虽然性能还行也跨平台了但是生态圈不发展起来也不行 刚出来的时候用 .Net Core + Dapper + Mysql 弄了 ...
- consul多数据中心搭建 【h】
自建IDC后面简称own.阿里云机房ali.腾讯云机房txown机房:内网10.10.10.0/24,边界节点,10.10.10.100/101.xxx.80.xxxali机房:内网10.10.10. ...
- Windows 7安装Service Pack 1失败问题
问题 很多朋友在电脑上安装某些软件时,会发现安装不了,提示信息大多是"本软件只支持 Windows 7 SP1 及更新版本"等等.这说明,你的电脑仍然在运行最早的Windows7版 ...
- android基础---->SharedPreferences的使用
SharedPreferences 还支持多种不同的数据类型存储,如果存储的数据类型是整型,那么读取出来的数据也是整型的,存储的数据是一个字符串,读取出来的数据仍然是字符串.这样你应该就能明显地感觉到 ...
- [CMD] 批处理
https://www.tutorialspoint.com/batch_script/batch_script_commands.htm
- C语言中的共用体(union)和枚举(enum)
1 union union Data{ int i; char ch; float f; }a={1, 'a', 1.5}; //错误 union Data a = {16}; //正确 union ...
- C++Primer 5th Chap4 Expressions
左值和右值:左值:用的是对象的身份(内存中的位置),右值:用的是对象的值(内容) 解引用与递增(递减)运算符连用: *ivec++:取ivec当前值并向后移动一个元素,等价于*(ivec++),本来+ ...
- 利用Matlab实现PID控制仿真
该文转自博客园: https://www.cnblogs.com/kui-sdu/p/9048534.html %PID Controller clear, clc, close all; ts=0. ...
- (转)Nginx+rtmp+ffmpeg搭建流媒体服务器
(1)下载第三方扩展模块nginx-rtmp-module # mkdir module && cd module //创建一个存放模块的目录 # wget https://githu ...
- LOJ2074/2157 JSOI2016/POI2011 Lightning Conductor 决策单调性DP
传送门 我们相当于要求出\(f_i = \max\limits_{j=1}^{n} (a_j + \sqrt{|i-j|})\).这个绝对值太烦人了,考虑对于\(i>j\)和\(i<j\) ...