P2577 [ZJOI2005]午餐
题目描述
上午的训练结束了,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的正整数。
Solution:
本题贪心+dp。
一个显然的贪心就是得先让吃饭时间长的人打饭,那么先把人按吃饭时间排序。
然后定义状态$f[i][j]$表示前$i$个人在第一个窗口打饭时间为$j$时的最快吃完饭时间,并用$s[i]$表示有序后的前缀打饭时间(便于转移时求出在第二个窗口的打饭时间)。
那么不难得到状态转移方程:$f[i][j]=min(max(f[i-1][j-a[i]],j+b[i]),max(f[i-1][j],s[i]-j+b[i]))$。
代码:
/*Code by 520 -- 10.23*/ #include<bits/stdc++.h> #define il inline #define ll long long #define RE register #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++) #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--) using namespace std; const int N=205; int n,f[N][N*N],s[N],ans=0x3f3f3f3f; struct node{ int a,b; il bool operator < (const node &a) const {return b>a.b;} }t[N]; int main(){ ios::sync_with_stdio(0); cin>>n; memset(f,0x3f,sizeof(f)); For(i,1,n) cin>>t[i].a>>t[i].b; sort(t+1,t+n+1); For(i,1,n) s[i]=s[i-1]+t[i].a; f[0][0]=0; For(i,1,n) For(j,0,s[i]) { if(j>=t[i].a) f[i][j]=min(f[i][j],max(f[i-1][j-t[i].a],j+t[i].b)); f[i][j]=min(f[i][j],max(f[i-1][j],s[i]-j+t[i].b)); } For(i,0,s[n]) ans=min(ans,f[n][i]); cout<<ans; return 0; }