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的好题 首先发现一个十分显然的性质,没有这个性质整道题目都难以下手: 无论两队的顺序如何,总是让吃饭慢的人先排队 这是一个很显然的贪心,因为如果让吃饭慢的排在后面要更多的时间至少没 ...
随机推荐
- 五、Snapman多人协作电子表格之——Python脚本
Snapman多人协作电子表格是一个即时工作系统. Snapman中嵌入了Python脚本进行数据处理. 一.Snapman集合python语言介绍 将单元格设置为python脚本的方法:用Snapm ...
- 【Django单元测试方法】
一.前言/准备 测Django的东西仅限于在MTV模型.哪些可以测?哪些不可以. 1.html里的东西不能测.①Html里的HTML代码大部分都是写死的②嵌套在html中的Django模板语言也不能测 ...
- Eureka学习笔记
解决: 自我保护: 消费端的调用: Euraka的集群:
- 超实用的 JavaScript 代码片段( ES6+ 编写)
Array 数组 Array concatenation (数组拼接) 使用 Array.concat() ,通过在 args 中附加任何数组 和/或 值来拼接一个数组. const ArrayCon ...
- android 8.0 以后 uiautomator 无法直接使用的问题
android8.1以后sdk tools自带的uiautomator直接打开,截取不到机器界面信息. 可以使用以下方法手动截取. 首先操作机器定位到要分析的界面. 1.截取uix资源文件 adb s ...
- Python 运算符 各类运算符总结
运算符详解2.1.算术运算符2.2.比较(关系)运算符2.3.赋值运算符2.4.逻辑运算符2.5.位运算符2.6.成员运算符2.7.身份运算符三.重要运算符说明3.1.join和符号”+“区别3.2. ...
- Linux基础系统优化(二)
SELinux功能 SELinux(Security-Enhanced Linux) 是美国国家安全局(NSA)对于强制访问控制的实现,这个功能管理员又爱又恨,大多数生产环境也是关闭的做法,安全手段使 ...
- docker(三):服务services
docker中services位于container上面,services可以控制image的运行方式,包括image运行时所需资源的大小 创建yml文件 yml文件定义了容器运行时的行为.我们先创建 ...
- css页面加载动画
<!doctype html> <html lang="en"> <head> <meta charset="utf-8&quo ...
- Codeforces 1207 G. Indie Album
Codeforces 1207 G. Indie Album 解题思路 离线下来用SAM或者AC自动机就是一个单点加子树求和,套个树状数组就好了,因为这个题广义SAM不能存在 \(len[u] = l ...