bzoj1899/zjoi2007 午餐

题目描述

上午的训练结束了,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

首先是贪心

首先不考虑两个队,我们注意到排队的顺序会对结果产生影响。
假设只有两个人,打饭时间为 a [ i ] a[i] a[i], 吃饭时间为 b [ i ] b[i] b[i]
那么吃饭的最晚时间是 a n s 1 = m a x ( a [ 1 ] + b [ 1 ] , <mtext>   </mtext> a [ 1 ] + a [ 2 ] + b [ 2 ] ) ans1=max(a[1]+b[1], ~a[1]+a[2]+b[2]) ans1=max(a[1]+b[1], a[1]+a[2]+b[2])
我们交换 1 2 1,2 12的位置,吃饭的最晚时间就是 a n s 2 = m a x ( a [ 2 ] + b [ 2 ] , <mtext>   </mtext> a [ 1 ] + a [ 2 ] + b [ 1 ] ) ans2=max(a[2]+b[2],~a[1]+a[2]+b[1]) ans2=max(a[2]+b[2], a[1]+a[2]+b[1])
我们要让最晚时间尽量小,即在 a n s 1 , a n s 2 ans1,ans2 ans1,ans2中取较小值
考虑到 a [ 1 ] + b [ 1 ] &lt; a [ 1 ] + a [ 2 ] + b [ 1 ] a[1]+b[1]&lt;a[1]+a[2]+b[1] a[1]+b[1]<a[1]+a[2]+b[1] a [ 2 ] + b [ 2 ] &lt; a [ 1 ] + a [ 2 ] + b [ 2 ] a[2]+b[2]&lt;a[1]+a[2]+b[2] a[2]+b[2]<a[1]+a[2]+b[2],所以 a n s 1 &lt; a n s 2 ans1 &lt;ans2 ans1<ans2的条件即是 a [ 1 ] + a [ 2 ] + b [ 2 ] &lt; a [ 1 ] + a [ 2 ] + b [ 1 ] a[1]+a[2]+b[2]&lt;a[1]+a[2]+b[1] a[1]+a[2]+b[2]<a[1]+a[2]+b[1],化简得 b [ 1 ] &gt; b [ 2 ] b[1]&gt;b[2] b[1]>b[2]
所以得到结论,只要我们按照吃饭时间从大到小排序,最终得到的结果一定比按其他顺序的优。


接下来看dp

那些东西是在变化的,足以表示一个状态的?到第 i i i个人肯定是要的,打饭时间肯定是要的。我之前一直想的是,要不要记录两个队的最后一个人是谁?最终发现是我多虑了,因为记录最后一个人对我们状态转移并没有帮助。再看看打饭时间,我们要记录两个队的吗?其实并不用,我们知道了一个队的打饭时间和,用前缀和减一减就可以得到另一个队的了。
所以我们记 f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i 个人, 一队打饭时间总和为 j j j, 两队的最小吃饭时间
可以得到转移方程
f [ i ] [ j ] = m a x ( f [ i 1 ] [ j ] , s [ i ] j + d [ i ] . b ) f[i][j] = max(f[i-1][j], s[i] - j + d[i].b) f[i][j]=max(f[i1][j],s[i]j+d[i].b)
f [ i ] [ j ] = m i n ( f [ i ] [ j ] , m a x ( f [ i 1 ] [ j d [ i ] . a ] , j + d [ i ] . b ) f[i][j] = min(f[i][j], max(f[i-1][j-d[i].a], j + d[i].b) f[i][j]=min(f[i][j],max(f[i1][jd[i].a],j+d[i].b)
这里可能会有人对第一个转移方程表示怀疑,觉得只是考虑了第 i i i 个的吃饭时间,前面的忘记考虑了。其实并不是的!第 i i i 个前面的时间已经记录在 f [ i 1 ] [ j ] f[i-1][j] f[i1][j] 里面了!我们的dp,永远是建立在前面状态是正确的基础上dp的(类似于数学归纳法)!

下面是代码

//f[i][j]表示前 i 个人, 一队打饭时间总和为 j, 两队的最小吃饭时间
//f[i][j] = max(f[i-1][j], s[i] - j + d[i].b)
//f[i][j] = min(f[i][j], max(f[i-1][j-d[i].a], j + d[i].b)
#include <bits/stdc++.h>
#define inf 2147483647 
#define N 205
using namespace std;
struct node{
	int a, b;
	bool operator < (const node & A) const{
		return b > A.b;
	}
}d[N];
int s[N], f[N][N * N], ans = inf;
int main(){
	int i, j, n, m;
	scanf("%d", &n);
	for(i = 1; i <= n; i++) scanf("%d%d", &d[i].a, &d[i].b);
	sort(d + 1, d + i);
	for(i = 1; i <= n; i++) s[i] = s[i-1] + d[i].a;
	memset(f, 1, sizeof(f));
	f[0][0] = 0;
	for(i = 1; i <= n; i++){
		for(j = 0; j <= s[i]; j++){
			f[i][j] = max(f[i-1][j], s[i] - j + d[i].b);
			if(j >= d[i].a) f[i][j] = min(f[i][j], max(f[i-1][j-d[i].a], j + d[i].b));
			if(i == n) ans = min(ans, f[i][j]);
		}
	}
	printf("%d", ans);
	return 0;
} 
各种题解及学习笔记~ 文章被收录于专栏

各种题解及学习笔记

全部评论

相关推荐

02-10 13:41
西南大学 Java
牛客12789347...:你们这28届的干啥呀,大学玩舒服了吗
点赞 评论 收藏
分享
02-07 12:06
已编辑
华侨大学 测试开发
最近看到很多&nbsp;92&nbsp;的,甚至是硕士,开始往测开赛道卷,说实话有点看不懂。先把话说清楚,大厂里的测开,绝大多数时间干的还是测试的活,只是写点自动化脚本、维护测试平台、接接流水线,真正像开发一样做系统、做架构、做核心平台的测开少得可怜,基本都集中在核心提效组,而且人很少,外面进去的大概率轮不到你,我想真正干过人都清楚。很多人被洗脑了,以为测开也是开,和后端差不多,只是更简单、更轻松、还高薪。现实情况是,测开和开发的职业路径完全不一样。开发的核心是业务和系统能力,测开的核心是稳定性和覆盖率,前者是往上走,后者天花板非常明显。你可以见到很多开发转测开,但你很少见到干了几年测开还能顺利转回开发的。更现实一点说,92&nbsp;的高学历如果拿来做测开,大部分时间就是在做重复性很强的杂活,这种工作对个人能力的放大效应非常弱。三年下来,你和一个双非的,甚至本科的测开差距不会太大,但你和同龄的后端、平台开发差距会非常明显。这不是努不努力的问题,是赛道问题。所谓测开简单高薪,本质上是把极少数核心测开的上限,当成了整个岗位的常态来宣传。那些工资高、技术强的测开,本身就是开发水平,只是挂了个测开的名。普通人进去,99%&nbsp;做的都是项目兜底型工作,而不是你想象中的平台开发。测开不是不能做,但它绝对不是开发的平替,也不是性价比最优解。如果你是真的不想做开发,追求稳定,那测开没问题。但如果你只是觉得测开比后端容易,还能进大厂,那我劝你冷静一点,这只是在用短期安全感换长期天花板。有92的学历,如果你连测开这些重复性工作都能心甘情愿接受,那你把时间精力用在真正的开发、系统、业务深度上,回报大概率比卷测开要高得多。想清楚再下场,别被岗位名和话术带偏了,就算去个前端客户端也是随便占坑的,测开是一个坑位很少赛道,反而大面积学历下放,不用想也能知道会是什么结果,我想各位在JAVA那里已经看到了
小浪_Coding:工作只是谋生的手段 而不是相互比较和歧视
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务