[Luogu P2577] [BZOJ 1899] [ZJOI2005]午餐

洛谷传送门
BZOJ传送门

题目描述

上午的训练结束了,THU ACM小组集体去吃午餐,他们一行 N N N人来到了著名的十食堂。这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭。由于每个人的口味(以及胃口)不同,所以他们要吃的菜各有不同,打饭所要花费的时间是因人而异的。另外每个人吃饭的速度也不尽相同,所以吃饭花费的时间也是可能有所不同的。

THU ACM小组的吃饭计划是这样的:先把所有的人分成两队,并安排好每队中各人的排列顺序,然后一号队伍到一号窗口去排队打饭,二号队伍到二号窗口去排队打饭。每个人打完饭后立刻开始吃,所有人都吃完饭后立刻集合去六教地下室进行下午的训练。

现在给定了每个人的打饭时间和吃饭时间,要求安排一种最佳的分队和排队方案使得所有人都吃完饭的时间尽量早。

假设THU ACM小组在时刻 0 0 0到达十食堂,而且食堂里面没有其他吃饭的同学(只有打饭的师傅)。每个人必须而且只能被分在一个队伍里。两个窗口是并行操作互不影响的,而且每个人打饭的时间是和窗口无关的,打完饭之后立刻就开始吃饭,中间没有延迟。

现在给定 N N N个人各自的打饭时间和吃饭时间,要求输出最佳方案下所有人吃完饭的时刻。

输入输出格式

输入格式:

第一行一个整数 N N N,代表总共有 N N N个人。

以下 N N N行,每行两个整数 A i A_i Ai B i B_i Bi。依次代表第 i i i个人的打饭时间和吃饭时间。

输出格式:

一个整数 T T T,代表所有人吃完饭的最早时刻。

输入输出样例

输入样例#1:
5
2 2
7 7
1 3
6 4
8 5
输出样例#1:
17

说明

所有输入数据均为不超过 200 200 200的正整数。

解题分析

首先, 如果分配给两个窗口的人一定, 因为最后一个人打饭的时间是一定的, 所以我们一定先让吃饭久的人先吃。

这样我们就可以先将所有人按吃饭速度从大到小排序, 然后考虑扔到两个窗口。 而因为打饭时间的前缀和是确定的, 我们可以设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i个人打饭用了 j j j分钟, 总用时的值, 我们就有
d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , m a x ( d p [ i − 1 ] [ j − A [ i ] ] , j + B [ i ] ) ) ( j ≥ A [ i ] , 放 在 第 一 个 窗 口 的 情 况 ) d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , m a x ( d p [ i − 1 ] [ j ] , s u m [ i ] − j + B [ i ] ) ) ( 放 在 第 二 个 窗 口 的 情 况 ) dp[i][j]=min(dp[i][j],max(dp[i-1][j-A[i]],j+B[i]))(j\ge A[i], 放在第一个窗口的情况) \\ dp[i][j] = min(dp[i][j],max(dp[i-1][j],sum[i]-j+B[i]))(放在第二个窗口的情况) dp[i][j]=min(dp[i][j],max(dp[i1][jA[i]],j+B[i]))(jA[i])dp[i][j]=min(dp[i][j],max(dp[i1][j],sum[i]j+B[i]))()
然后就可以开始 D P DP DP了。

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 205
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int dp[MX][MX * MX], sum[MX];
struct INFO {int a, b;} dat[MX];
IN bool operator < (const INFO &x, const INFO &y)
{return x.b > y.b;}
int dot, bd, ans = 100000000;
int main(void)
{
	std::memset(dp, 63, sizeof(dp));
	in(dot); bd = dot * 200;
	for (R int i = 1; i <= dot; ++i) in(dat[i].a), in(dat[i].b);
	std::sort(dat + 1, dat + 1 + dot);
	for (R int i = 1; i <= dot; ++i) sum[i] = sum[i - 1] + dat[i].a;
	dp[0][0] = 0;
	for (R int i = 1; i <= dot; ++i)
	{
		for (R int j = 0; j <= bd; ++j)
		{
			if(j >= dat[i].a) dp[i][j] = std::max(dp[i - 1][j - dat[i].a], j + dat[i].b);
			dp[i][j] = std::min(dp[i][j], std::max(dp[i - 1][j], sum[i] - j + dat[i].b));
		}
	}
	for (R int i = 0; i <= bd; ++i) ans = std::min(ans, dp[dot][i]);
	printf("%d", ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值