【贪心+DP】BZOJ1899(Zjoi2004)[Lunch 午餐]题解

题目概述

有n个人,2个窗口,每个人到1窗口和2窗口的打饭时间和吃饭时间都是一样的。问所有人吃好饭的最早时间。

解题报告

如果只有一个窗口,那就是从大到小排吃饭时间,然后按照这个顺序排队(某基础贪心题,好像叫突击战)。但是两个窗口怎么办?原来的贪心策略肯定还要保留,但是如何实现两个窗口呢?由于n很小,我们会想到DP。但是状态好像很难定义,因为哪个人是哪个队的如果要存下来是不可能的。思考一下我们发现哪个人是哪个队的不需要知道,只需要知道某个队目前的最大时间就行了!定义f[i][j]表示前i个中,第一个窗口的打饭总时间为j的最优解。那么对于i,分为两种状态(sum[i]表示1~i打饭时间的总和):
1.放窗口2
上一状态:max(f[i-1][j],sum[i-1]-j+i的打饭时间+i的吃饭时间)
其中f[i-1][j]是上一状态的最优解,后面那些是i放窗口2的代价。
2.放窗口1
上一状态:max(f[i-1][j-i的打饭时间],j-i的打饭时间+i的打饭时间+i的吃饭时间)
其中f[i-1][j-i的打饭时间]是上一状态的最优解,后面那些是i放窗口1的代价。

示例程序

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200,maxm=40000,MAXINT=((1<<30)-1)*2+1;

int n,f[maxn+5][maxm+5],sum[maxn+5],ans;
struct Student
{
    int a,b;
    bool operator < (const Student &a) const {return b>a.b;}
};
Student s[maxn+5];

int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    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,63,sizeof(f));
    f[0][0]=0;
    for (int i=1;i<=n;i++)
    for (int j=0;j<=sum[i];j++)
    {
        f[i][j]=min(f[i][j],max(f[i-1][j],sum[i-1]-j+s[i].a+s[i].b));
        if (s[i].a<=j) f[i][j]=min(f[i][j],max(f[i-1][j-s[i].a],j+s[i].b));
    }
    ans=MAXINT;
    for (int j=0;j<=sum[n];j++) ans=min(ans,f[n][j]);
    printf("%d\n",ans);
    return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值