题目描述:
两个窗口,每个人有打饭时间
a
i
a_i
ai和吃饭时间
b
i
b_i
bi,安排队伍顺序,使得从0时刻到最后一个吃完的总用时最少。
n
<
=
200
,
a
i
,
b
i
<
=
200
n<=200,a_i,b_i<=200
n<=200,ai,bi<=200
题目分析:
显然需要一个排序,于是大家非常愉快地就让吃饭时间长的人在前面了。
然而我太菜了,不能一眼看出这个结论,只能胡乱分析。
考虑两个人
a
1
,
b
1
a_1,b_1
a1,b1和
a
2
,
b
2
a_2,b_2
a2,b2,如果要1号排在2号前面比2号排在1号前面优,就要有:
max
(
a
1
+
b
1
,
a
1
+
a
2
+
b
2
)
≤
max
(
a
2
+
b
2
,
a
1
+
a
2
+
b
1
)
\max(a_1+b_1,a_1+a_2+b_2)\le\max(a_2+b_2,a_1+a_2+b_1)
max(a1+b1,a1+a2+b2)≤max(a2+b2,a1+a2+b1)。
显然前者的前一项是一定小于等于后者的后一项的,所以只要前者的后一项小于等于后者的后一项,即
a
1
+
a
2
+
b
2
≤
a
1
+
a
2
+
b
1
a_1+a_2+b_2\le a_1+a_2+b_1
a1+a2+b2≤a1+a2+b1(化简一下就是
b
2
≤
b
1
b_2\le b_1
b2≤b1),上式就一定成立。
如果
b
1
=
b
2
b_1=b_2
b1=b2,那么无论谁排前面,总耗时都是
a
1
+
a
2
+
b
a_1+a_2+b
a1+a2+b。
两个人对于后面的影响与他们的顺序无关,所以只需要按照b从大到小排序就可以了。
考虑了前k个人是 f [ i ] f[i] f[i]表示第一个窗口的打饭时间为 i i i的最少总用时,另一个窗口的打饭时间可以直接算,也就是知道打饭时间就知道这个人吃完饭的时间,所以就可以DP了。
Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 205
using namespace std;
int n,f[maxn*maxn];
struct node{
int x,y;
bool operator < (const node &p)const{return y>p.y;}
}a[maxn];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
sort(a+1,a+1+n);
memset(f,0x3f,sizeof f),f[0]=0;
int sum=a[1].x;
for(int i=1;i<=n;i++,sum+=a[i].x)
for(int j=sum;j>=0;j--){
f[j]=max(f[j],sum-j+a[i].y);
if(j>=a[i].x) f[j]=min(f[j],max(f[j-a[i].x],j+a[i].y));
}
printf("%d\n",*min_element(f,f+1+sum));
}