[ZJOI2005] 午餐

Posted hkttg

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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的正整数。

 

为了简化问题,我们先来考虑一对怎么搞

首先,我们需要一个顺序。也就是划分阶段

类似于排队接水的顺序,但需要换一个角度思考

由于每个人打完饭就跑路开吃,所以所有人总共要打饭的时间是固定的,就是把所有人的打饭时间加起来

那么我们考虑,如果仅有两人AB,他们的打饭时间和吃饭时间都不同,是A在前快还是B在前快?

画个图就知道,很明显是吃饭时间长的排在前面比较快。

技术分享图片

for(int i = 1; i <= n; ++ i)
    cin >> a[i] >> b[i], p[i] = i;
sort(p+1, p+n+1, cmp); // 按照吃饭时间排序

得到这个结论后,就可以搞DP了

对于每个人,我们考虑是去第一队还是第二队两种情况

用f[i]表示考虑到第i 个人, 目前最优的吃饭时间

状态补充一下,用f[i][j][k]表示第一条队总时长为j, 第二条队总时长为k

到这里DP就差不多了

但是我们发现

三维数组造成了很大的空间开销

能不能数组压缩为二维数组呢?

在考虑顺序时我们说过,总的排队时间是不变的

也就是说,如果知道在一队的全部人,那么我们也就知道了在二队的全部人,因为二队人的排队时间和等于总和减去一队人的排队时间和

f[i+1][j+A] = min(f[i+1][j+A], max(f[i][j], j+A+B)); // 排第一条队
f[i+1][j] = min(f[i+1][j], max(f[i][j], s[i-1]-j+A+B)); // 排第二条队

只需要在DP前搞一下前缀和就好了

for(int i = 1; i <= n; ++ i) // 求前缀和
    s[i] = s[i-1] + a[p[i]];

这样我们就可以用二维数组解决掉这道题

当然你也可以用两个不同的数组表示一队和二队

这里不再赘述。

下面是完整代码

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 205;
const int M = N * N;
const int INF = 1e9;

int n, f[N][M], sum[N]; 

struct Edge {
    int A, B;
}T[N];

bool cmp(Edge x, Edge y) { return x.B > y.B;}

int main() {
    scanf("%d", &n); 
    for (int i = 1; i <= n; i++) 
        scanf("%d%d",&T[i].A, &T[i].B);
    sort(T + 1, T + n + 1, cmp);
    for (int i = 1; i <= n; i++) 
        sum[i] = sum[i-1] + T[i].A;
    int m = n * N;
    for(int i = 1; i <= n + 1; i++) 
        for(int j = 0; j <= m; j++) f[i][j] = INF;
    f[1][0] = 0; 
    for (int i = 1; i <= n; i++) 
        for (int j = 0; j <= m; j++) 
            if (f[i][j] < INF) {
                f[i+1][j+T[i].A] = min(f[i+1][j+T[i].A], max(f[i][j], j + T[i].A + T[i].B));
                f[i+1][j] = min(f[i+1][j], max(f[i][j], sum[i-1] - j + T[i].A + T[i].B));
            }
    int ans = INF;
    for(int i = 0; i <= m; i++) ans = min(ans, f[n+1][i]);
    printf("%d", ans);
    return 0;
}

 

以上是关于[ZJOI2005] 午餐的主要内容,如果未能解决你的问题,请参考以下文章

Luogu P2577 [ZJOI2005]午餐(dp)

[ZJOI2005] 午餐

[ZJOI2005]午餐

ZJOI2005午餐

[ZJOI2005]午餐 (贪心,动态规划)

P2577 [ZJOI2005]午餐