P2577 [ZJOI2004]午餐

知识点: DP

原题面

不会真的有人 2h 一道绿题都调不出来吧,不会吧不会吧。
是我没错了


分析题意

设第 \(i\) 个人打饭时间,吃饭时间分别为 \(a_i\)\(b_i\),前 \(i\) 个人打饭时间总和为 \(sum_i\)

先考虑 只排一队 的情况,对于一给定的队列完成的时间,有:

\[time = \max_{i=1}^{n}\{sum_i+b_i\} \]


考虑两个相邻的人 \(i\)\(i+1\),若有 \(b_{i+1} > b_i\)

  • \(i+1\) 在后面时,两者完成时间分别为:

    \[\begin{aligned} & sum_{i-1} + (a_i + b_i)\\ & sum_{i-1} + a_i + (a_{i+1}+b_{i+1}) \end{aligned}\]

    显然第 \(i+1\) 个人一定后吃完,即有下式成立:

    \[sum_{i-1} + a_i +(a_{i+1}+b_{i+1})> sum_{i-1} + (a_i + b_i) \]

  • \(i+1\) 在前面时,完成时间分别为:

    \[\begin{aligned} & sum_{i-1}+ (a_{i+1} + b_{i+1})\\ & sum_{i-1} + a_{i+1}+ (a_i + b_i) \end{aligned}\]

    两人吃完的先后顺序不定。

显然有:

\[\begin{aligned} & sum_{i-1} + a_i +(a_{i+1}+b_{i+1})> \\ & \max\{sum_{i-1}+ (a_{i+1} + b_{i+1}), sum_{i-1} + a_{i+1}+ (a_i + b_i)\} \end{aligned}\]


则欲使 \(\max\limits_{i=1}^{n}\{sum_i+b_i\}\) 尽可能小,令\(i+1\) 排在前面更优。
感性理解一下,吃饭慢的放在前面一定更优。

则可先将 \(n\) 个人按照吃饭时间降序排序,逐个加入队列的人变成有序的了。
可以考虑线性 DP 求解此题。


发现有两边,不好设状态。
考虑 P1973 的思路,枚举一边的答案,并求另一边答案的最小值,两边取 \(max\) 即为所求。
\(f_{i,j,k}\) 表示 \(1\sim i\) 中,窗口一最后一个人为 \(j\),完成时间为 \(k\) 时,窗口二的完成时间。

然后发现无法转移,因为最后一个人不一定 最晚吃完。
陷入思考...

瞎奖杯画

手搓的心心.png


发现窗口二的队列,必定为窗口一的补集。

设当前第 \(i\) 个人加入队列,令窗口一队列 打饭时间总和为 \(j\),则窗口二 打饭时间 总和为 \(sum_i - j\)

若已知 \(j\),则可计算第 \(i\) 个人加入窗口 一/二 的 完成时间
考虑枚举窗口一打饭时间总和 \(j\),来更新答案。


\(f_{i,j}\) 表示,前 \(i\) 个人加入队列,窗口一队列打饭时间总和为 \(j\)时,两窗口的最小完成时间。

考虑第 \(i\) 个人排到窗口 一/二:

  1. 排到窗口一,则有:

    \[f_{i,j} = \max\{j + b_i,\ f_{i-1,j-a_i}\} \]

    \(j + b_i\) 为新加入窗口一的人的完成时间。
    \(f_{i-1,j-a_i}\) 为:不加此人时,窗口一的完成时间 与 窗口二的完成时间 的最小值,用于更新答案,不会导致漏解。
  2. 排到窗口二,则有:

    \[f_{i,j} = \max\{f_{i-1,j},\ sum_i-j+b_i\} \]

    \(sum_i-j+b_i\) 为新加入窗口二的人的完成时间。
    \(f_{i-1,j}\) 也为不加此人时,窗口一的完成时间 与 窗口二的完成时间 的最小值。

上述两种情况取最小值即可。
复杂度 \(O(nT)\) (\(T\) 为最大时间),期望得分 \(100\text{pts}\)


代码实现

//知识点:DP
/*
By:Luckyblock
*/
#include <cstdio>
#include <ctype.h>
#include <cstring>
#include <algorithm>
#define min std::min
#define max std::max
#define ll long long
const int kMaxn = 210;
const int kInf = 1e9 + 2077;
//=============================================================
struct Data {
  int a, b;
} d[kMaxn];
int n, ans = kInf, sum[kMaxn], f[kMaxn][kMaxn * kMaxn];
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
bool Compare(Data fir, Data sec) {
  return fir.b > sec.b;
}
//=============================================================
int main() {
  n = read();
  for (int i = 1; i <= n; ++ i) {
    d[i].a = read(), d[i].b = read();
  }
  std :: sort(d + 1, d + n + 1, Compare);
  memset(f, 63, sizeof(f));
  f[0][0] = 0;
  for (int i = 1; i <= n; ++ i) sum[i] = sum[i - 1] + d[i].a;
  for (int i = 1; i <= n; ++ i) {
    int a = d[i].a, b = d[i].b;
    for (int j = 1; j <= sum[i]; ++ j) {
      f[i][j] = max(f[i - 1][j], sum[i] - j + b);
      if (j - a >= 0) f[i][j] = min(f[i][j], max(f[i - 1][j - a], j + b));
    }
  }
  for (int i = 1; i <= sum[n]; ++ i) {
    ans = min(ans, f[n][i]);
  }
  printf("%d\n", ans);
  return 0;
}
posted @ 2020-07-08 19:30  Luckyblock  阅读(129)  评论(6编辑  收藏  举报