BZOJ1899: [Zjoi2004]Lunch 午餐【贪心DP】

21 篇文章 0 订阅
题目描述:

两个窗口,每个人有打饭时间 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+b2a1+a2+b1(化简一下就是 b 2 ≤ b 1 b_2\le b_1 b2b1),上式就一定成立。
如果 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));
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值