洛谷 P2577 [ZJOI2005]午餐 解题报告

题面

上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂。这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭。由于每个人的口味(以及胃口)不同,所以他们要吃的菜各有不同,打饭所要花费的时间是因人而异的。另外每个人吃饭的速度也不尽相同,所以吃饭花费的时间也是可能有所不同的。

THU ACM小组的吃饭计划是这样的:先把所有的人分成两队,并安排好每队中各人的排列顺序,然后一号队伍到一号窗口去排队打饭,二号队伍到二号窗口去排队打饭。每个人打完饭后立刻开始吃,所有人都吃完饭后立刻集合去六教地下室进行下午的训练。

现在给定了每个人的打饭时间和吃饭时间,要求安排一种最佳的分队和排队方案使得所有人都吃完饭的时间尽量早。

假设THU ACM小组在时刻0到达十食堂,而且食堂里面没有其他吃饭的同学(只有打饭的师傅)。每个人必须而且只能被分在一个队伍里。两个窗口是并行操作互不影响的,而且每个人打饭的时间是和窗口无关的,打完饭之后立刻就开始吃饭,中间没有延迟。

现在给定N个人各自的打饭时间和吃饭时间,要求输出最佳方案下所有人吃完饭的时刻。

输入输出格式

输入格式:

第一行一个整数N,代表总共有N个人。

以下N行,每行两个整数 Ai,Bi。依次代表第i个人的打饭时间和吃饭时间。

输出格式:

一个整数T,代表所有人吃完饭的最早时刻。

输入输出样例

输入样例#1:  复制
5
2 2
7 7
1 3
6 4
8 5
输出样例#1:  复制
17

蒟蒻的解题思路:

首先,如果只有一队的话,整队的打饭时间是固定的。那么,我们让吃饭时间长的人先打饭

设a[i].x表示第i个人的打饭时间,a[i].y表示第i个人的吃饭时间
同理,设a[j].x表示第j个人的打饭时间,a[j].y表示第j个人的吃饭时间
并且a[i].y>a[j].y。

假设j在前面,
t1=max(a[j].x+a[i].x+a[i].y,a[j].y+a[j].x)
max中的前者表示j吃完饭后i还没有吃完;后者表示j吃完饭前i就吃完了。
同理,i在前面的话
t2=max(a[i].x+a[j].x+a[j].y,a[i].y+a[i].x)

t1=max(a[i].x+a[i].y,a[j].y)+a[j].x
∵a[i].y>a[j].y
∴以a[i].x+a[i].y>a[j].y
故t1=a[i].x+a[j].x+a[i].y

t2=max(a[j].x+a[j].y,a[i].y)+a[i].x

若t2=a[i].x+a[j].x+a[j].y,
∵a[i].y>a[j].y
所以t1>t2
若t2=a[i].x+a[i].y
t1显然>t2

因此当a[i].y>a[j].y时,i在前面更优。

证毕。
贪心证明

设dp[i]表示在一窗口打饭i时间,最后的总时间

当我们枚举到第i个人,此时在一号打饭j时间
i既可以去一号窗口,也可以去二号窗口。

因此进行分类讨论:

  • 若第i个人在一号打饭:

  如果前面的人吃完饭的时间比第i个人吃完饭的时间长
  那么  dp[j+a[i].x]=min(dp[j])

  反之,前面的人在第i个人吃完饭之前吃完
  那么  dp[j+a[i].x]=min(j+a[i].x+a[i].y)

  因此状态转移方程为:  dp[j+a[i].x]=min(dp[j+a[i].x],max(dp[j],j+a[i].x+a[i].y));

  • 若第i个人在二号打饭:

  用sum[i]维护前i个人的打饭时间,那么sum[i]-j就表示前面所有人在2号打饭的时间
  与上面相同的思路来进行分类讨论:

  如果第i个人吃完,前j时间的人仍没有吃完
  那么dp[j]不变。

  反之,第i个人吃完之前前面j时间的人已经吃完了
  那么dp[j]=(sum[i]-j)+a[i].y

  因此状态转移方程为:  dp[j]=max(dp[j],(sum[i]-j)+a[i].y);


代码如下:

#include <bits/stdc++.h>
#define endl "\n"
using namespace std;

inline void read(int &a)
{
    a=0;int c=getchar();
    while(c>'9'||c<'0') c=getchar();
    while(c>='0'&&c<='9') a=(a<<3)+(a<<1)+c-48,c=getchar();
}

const int N=205;
int n,dp[N*N],sum[N],ans=0x7f7f7f7f;
struct node
{
    int x,y;
    bool operator<(const node b)const
    {
        return y>b.y;
    }
}a[N];

namespace chu_xuan
{
    void main()
    {
        read(n);
        for(int i=1;i<=n;i++)
        {
            read(a[i].x);
            read(a[i].y);
        }
        sort(a+1,a+1+n);
        memset(dp,0x3f,sizeof(dp));
        dp[0]=0;
        for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i].x;
        
        for(int i=1;i<=n;i++)
        {
            for(int j=sum[i-1];j>=0;j--)
            {
                dp[j+a[i].x]=min(dp[j+a[i].x],max(dp[j],j+a[i].x+a[i].y));
                dp[j]=max(dp[j],sum[i]-j+a[i].y);
            }
        }
        
        for(int i=1;i<=sum[n];i++)
        {
            ans=min(ans,dp[i]);
        }
        cout<<ans<<"\n";
    }
}

int main()
{
    chu_xuan::main();
    return 0;
}
View Code










猜你喜欢

转载自www.cnblogs.com/chu-xuan/p/10087182.html