Description

上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂。这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭。由于每个人的口味(以及胃口)不同,所以他们要吃的菜各有不同,打饭所要花费的时间是因人而异的。另外每个人吃饭的速度也不尽相同,所以吃饭花费的时间也是可能有所不同的。 THU ACM小组的吃饭计划是这样的:先把所有的人分成两队,并安排好每队中各人的排列顺序,然后一号队伍到一号窗口去排队打饭,二号队伍到二号窗口去排队打饭。每个人打完饭后立刻开始吃,所有人都吃完饭后立刻集合去六教地下室进行下午的训练。 现在给定了每个人的打饭时间和吃饭时间,要求安排一种最佳的分队和排队方案使得所有人都吃完饭的时间尽量早。 假设THU ACM小组在时刻0到达十食堂,而且食堂里面没有其他吃饭的同学(只有打饭的师傅)。每个人必须而且只能被分在一个队伍里。两个窗口是并行操作互不影响的,而且每个人打饭的时间是和窗口无关的,打完饭之后立刻就开始吃饭,中间没有延迟。 现在给定N个人各自的打饭时间和吃饭时间,要求输出最佳方案下所有人吃完饭的时刻。

Input

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

Output

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

Sample Input

5
2 2
7 7
1 3
6 4
8 5

Sample Output

17

HINT

方案如下:

窗口1: 窗口2:
7 7 1 3
6 4 8 5
2 2

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

Source

题解

这题没有自己想出来QAQ

事实上如果只有一个窗口那就是经典贪心题,对吃饭时间进行排序,吃饭时间长的先打饭(因为总的打饭时间是一定的)

先保留上面的想法,想想其他做法

我们可以设$f[i][j][k]$表示前i个人在1窗口打饭时间为j,在2窗口打饭为k的最优解

但是j和k这两维都得开到40000(200*200)

考虑优化空间。发现只要对打饭时间维护一个前缀和,那么就可以只保存一个时间了,另外一个时间则是$sum[i]-j$

考虑转移(方程里面的c[i]是前缀和)

对于在1窗口打饭的情况:$f[i][j]=min(f[i][j],max(f[i-1][j-a[i].x],j+a[i].y))$ 

对于在2窗口打饭的情况:$f[i][j]=min(f[i][j],max(f[i-1][j],c[i]-j+a[i].y))$

那么最优解就是在$f[n][1...c[n]]$里面找了

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

struct node {
    int x,y;
}a[300];

int h1,h2,n;
int c[300],f[300][40000];
//f[i][j]表示前i个人时其中一个队伍打饭时间为j的最优解 
//c数组维护前缀和 

bool cmp(node a,node b){
    return a.y>b.y;
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&a[i].x,&a[i].y);
    }
    
    memset(f,0x3f,sizeof(f));
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)c[i]=c[i-1]+a[i].x;
    
    f[0][0]=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=c[i];j++){
            f[i][j]=min(f[i][j],max(f[i-1][j],c[i]-j+a[i].y));
            //在另外一个窗口吃 
            if(j-a[i].x>=0)f[i][j]=min(f[i][j],max(f[i-1][j-a[i].x],j+a[i].y));
            //在当前窗口吃 
        }
    }
    
    int ans=0x3f3f3f3f;
    for(int i=0;i<=c[n];i++){
        ans=min(ans,f[n][i]);
    }
    printf("%d\n",ans);
    
    return 0;
}