#(排序+线性DP)洛谷P2577 [ZJOI2005]午餐(提高+/省选-)

题目描述

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

分析:首先,吃饭越慢的越先打饭,贪心思想正确性显然;

先按照吃饭时间排一下序。然后考虑状态的设计:

需要维护:当前的人序号,1号窗时间,2号窗时间,总共最大时间;

设f[i][j][k]表示前i个人,在1花j时间,2花k时间的最大时间;

(考虑降维?维护一个前缀和数组,存储排序后的打饭时长!那么k+j=sum[i],k=sum[i]-j,成功降维!)

f[i][j]存储前i个人在1号花j分钟的最大吃饭时间;

ans=min(f[n][x]) x∈[0,sum[n]];

考虑状态转移方程:对于第i个人来说,他可以到1号或者2号窗口,并有可能对原先的1或2号窗口的最大时间产生影响

1号:当j>=s[i].a时,即第i个人可以在j时刻之前打完饭,那么考虑对1号影响:

f[i][j]=min(f[i][j],max(f[i-1][j-s[i].a],j+s[i].b));后面是影响!

要么最长时间从前i-1个人中产生,要么有第i个人产生影响!

2号:

对于任意情况,f[i][j]=min(f[i][j],max(f[i-1][j],sum[i]-j+s[i].b));(考虑2号窗口的影响,若超过已知,则更新)

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 210;
int f[N][N*N];
struct node
{
int a, b;
bool operator <(node z) const
{
return b>z.b;
}
}s[N];
int sum[N];
int main()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d %d", &s[i].a, &s[i].b);
sort(s+1, s+1+n);
for(int i = 1; i <= n; i++)
sum[i] = sum[i-1] + s[i].a;
memset(f, 127, sizeof(f));
f[0][0] = 0;
for(int i = 1; i <= n; i++)
{
for(int j = 0; j <= sum[i]; j++)
{
if(j>=s[i].a) f[i][j] = min(f[i][j], max(f[i-1][j-s[i].a], j+s[i].b));
f[i][j] = min(f[i][j], max(f[i-1][j], sum[i]-j+s[i].b));
}
}
int ans = 2147483647;
for(int i = 0; i <= sum[n]; i++)
ans = min(ans, f[n][i]);
printf("%d\n", ans);
return 0;
}

 

猜你喜欢

转载自www.cnblogs.com/little-cute-hjr/p/11441729.html
今日推荐