BZOJ 1899: [Zjoi2004]Lunch 午餐 DP

题意

n个人,每个人有打饭时间和吃饭时间,有两个打饭队列,求最后一个人吃完饭的时间最小值。
n,吃饭时间,打饭时间 <= 200

题解

发现数据范围小,可以想到n^3算法。

首先对于一个队列,内部顺序一定是吃饭吃的慢的放在前面。所以先按b从大到小排序。

设f[i][j]表示前i个人,1号队列总打饭时间为j时的 最慢的人吃完的时间。答案就是f[n][j]的最小值。

设a为打饭时间,b为吃饭时间,转移显然:

  • f[i][j+b[i]]=min(f[i][j+b[i]],max(f[i-1][j],j+a[i]+b[i]))
  • f[i][j]=max(f[i-1][j],suma[i-1]-j+a[i]+b[i])

因为求出a的前缀和,知道1号队列打饭时间,就知道2号队列打饭时间。

再加上滚动。

有点模拟思想的DP。

CODE

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 205;
struct node {
    int a, b;
    inline bool operator <(const node &o)const {
        return b > o.b;
    }
}p[MAXN];
int n, dp[MAXN*MAXN];
int main () {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d%d", &p[i].a, &p[i].b);
    sort(p + 1, p + n + 1);
    memset(dp, 0x3f, sizeof dp);
    dp[0] = 0; int sum = 0;
    for(int i = 1; i <= n; ++i) {
        for(int j = sum; j >= 0; --j) {
            dp[j + p[i].a] = min(dp[j + p[i].a], max(dp[j], j + p[i].a + p[i].b));
            dp[j] = max(dp[j], sum - j + p[i].a + p[i].b);
        }
        sum += p[i].a;
    }
    int ans = 1000000000;
    for(int j = 0; j <= sum; ++j)
        ans = min(ans, dp[j]);
    printf("%d\n", ans);
}
posted @ 2019-12-14 14:50  _Ark  阅读(80)  评论(0编辑  收藏  举报