描述
上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂。这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭。由于每个人的口味(以及胃口)不同,所以他们要吃的菜各有不同,打饭所要花费的时间是因人而异的。另外每个人吃饭的速度也不尽相同,所以吃饭花费的时间也是可能有所不同的。
THU ACM小组的吃饭计划是这样的:先把所有的人分成两队,并安排好每队中各人的排列顺序,然后一号队伍到一号窗口去排队打饭,二号队伍到二号窗口去排队打饭。每个人打完饭后立刻开始吃,所有人都吃完饭后立刻集合去六教地下室进行下午的训练。
现在给定了每个人的打饭时间和吃饭时间,要求安排一种最佳的分队和排队方案使得所有人都吃完饭的时间尽量早。
假设THU ACM小组在时刻0到达十食堂,而且食堂里面没有其他吃饭的同学(只有打饭的师傅)。每个人必须而且只能被分在一个队伍里。两个窗口是并行操作互不影响的,而且每个人打饭的时间是和窗口无关的,打完饭之后立刻就开始吃饭,中间没有延迟。
现在给定N个人各自的打饭时间和吃饭时间,要求输出最佳方案下所有人吃完饭的时刻。
输入
第一行一个整数N,代表总共有N个人。 以下N行,每行两个整数 Ai,Bi。依次代表第i个人的打饭时间和吃饭时间。
输出
一个整数T,代表所有人吃完饭的最早时刻。
样例输入
5
2 2
7 7
1 3
6 4
8 5
样例输出
17
提示
所有输入数据均为不超过200的正整数。
样例说明 方案如下: 窗口1: 窗口2: 7 7 1 3 6 4 8 5 2 2
标签
ZJOI2005
一道简单的线性dp。
直接f[i][j]f[i][j]f[i][j]表示前i个人,第一组打饭一共j分钟所需要的最少时间。
由于所有人打饭的总时间是相同的。
因此只跟吃饭时间有关系。
于是我们按吃饭时间从大到小排序。
然后就是简单01背包分类讨论了。
代码:
#include<bits/stdc++.h>
#define N 50005
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
int n,f[2][N],tmp,sum[N];
struct node{int a,b;}peo[N];
inline bool cmp(node a,node b){return a.b>b.b;}
int main(){
n=read();
for(int i=1;i<=n;++i)peo[i].a=read(),peo[i].b=read();
sort(peo+1,peo+n+1,cmp),tmp=0;
for(int i=1;i<=n;++i)sum[i]=sum[i-1]+peo[i].a;
for(int i=1;i<=n;++i){
tmp^=1,memset(f[tmp],0x3f,sizeof(f[tmp]));
for(int j=0;j<=sum[i];++j){
if(j>=peo[i].a)f[tmp][j]=min(f[tmp][j],max(j+peo[i].b,f[tmp^1][j-peo[i].a]));
if(sum[i]-j>=peo[i].a)f[tmp][j]=min(f[tmp][j],max(f[tmp^1][j],sum[i]-j+peo[i].b));
}
}
int ans=0x3f3f3f3f;
for(int i=0;i<=sum[n];++i)ans=min(ans,f[tmp][i]);
cout<<ans;
return 0;
}