• [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

    说明

    所有输入数据均为不超过200的正整数。


    Solution

    考虑贪心.


    给出贪心条件证明:
    令当前,有两个人分别为 a,b,且满足 a 在 b 前为更优解.
    排队和吃饭时间分别为:

    [d_a,c_a,d_b,c_b ]

    那么当前如果 a 在 b前,所需要花费的时间即为:

    [d_a+max(c_a,d_b+c_b) ]

    同理,如果 b 在 a 前,所需花费的时间为:

    [d_b+max(c_b,d_a+c_a) ]

    因为满足 a 在 b 前条件更优,即满足关系:

    [d_a+max(c_a,d_b+c_b)<d_b+max(c_b,d_a+c_a) ]


    以上贪心是一列队的做法,对于两列,考虑DP. **定义状态:** $$f[i][j]$$ 表示到了第 i 个人,第1队**打饭时间** (不包括吃饭)为 j 时的最小集合时间.

    转移方程
    对于第 i 个人,它有两种情况.

    1. 去第一队

    [f[i+1][j+a[i+1].w]=min(f[i+1][j+a[i+1].d],max(j+a[i+1].d+a[i+1].c,f[i][j])); ]

    1. 去第二队

    [f[i+1][j]=min(f[i+1][j],max(f[i][j],a[i+1].c+sum[i]-j+a[i+1].d)); ]

    其中 sum 代表排序之后的排队前缀和.

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=208;
    struct sj{
        int c;
        int d;
    }a[maxn];
    bool cmp(sj s,sj j)
    {return s.d+max(s.c,j.c+j.d)<j.d+max(j.c,s.c+s.d);}
    
    int n,sum[maxn];
    int f[maxn][maxn*maxn];
    int main()
    {
        ios::sync_with_stdio(false);
        cin>>n;
        for(int i=1;i<=n;i++)
        cin>>a[i].d>>a[i].c;
        sort(a+1,a+n+1,cmp);
        for(int i=1;i<=n;i++)
        sum[i]=sum[i-1]+a[i].d;
        memset(f,127,sizeof(f));
        int inf=f[0][0];
        f[0][0]=0;
        for(int i=0;i<n;i++) 
        for(int j=0;j<=sum[i];j++) 
        {
            if(f[i][j]==inf)
            continue;
            f[i+1][j+a[i+1].d]=min(f[i+1][j+a[i+1].d],max(j+a[i+1].d+a[i+1].c,f[i][j]));
            f[i+1][j]=min(f[i+1][j],max(f[i][j],a[i+1].c+sum[i]-j+a[i+1].d));
        }
        int ans=19260817;
        for(int i=0;i<=sum[n];i++)
        ans=min(ans,f[n][i]);
        cout<<ans<<endl; 
        return 0;
    }
    
  • 相关阅读:
    Xilinx之软件平台 ISE与Vivado前世今生
    博客开园
    第一天:开始你的Jobeet项目
    MySQL之alter语句用法总结
    SELECT INTO 和 INSERT INTO SELECT 两种表复制语句
    MySQL中distinct和group by性能比较[转]
    GROUP BY,WHERE,HAVING之间的区别和用法
    split(),preg_split()与explode()函数分析与介
    解析posix与perl标准的正则表达式区别
    sql关键字的解释执行顺序
  • 原文地址:https://www.cnblogs.com/Kv-Stalin/p/9193539.html
Copyright © 2020-2023  润新知