题意
现在有 n 个工人要粉刷一段篱笆,每个工人可以粉刷包含 starti 的一段最长不超过 leni 的篱笆(当然也可以不粉刷),每个工人粉刷长度为 1 的篱笆的收益为 costi,求最大收益
 思路
考虑 dp,设 dpi,j 表示前 i 个人粉刷前 j 段篱笆可以获得的最大收益
显然有如下的暴力转移
dpi,jdpi,jdpi,j←min{dpi−1,k+(j−k)×costi},←dpi−1,j,←dpi,j−1,(让第 i 个人粉刷区间 (k,j]),(第 i 个人不粉刷),(第 i 个人不粉刷).
观察发现,瓶颈在于如何快速求出 min{dpi−1,k+(j−k)×costi}
对上式进行变形,可得
min{dpi−1,k+(j−k)×costi}==min{dpi−1,k+j×costi−k×costi}min{dpi−1,k−k×costi}+j×costi
即上式可以被分为两个部分,仅包含 i,j 的和仅包含 i,k 的部分,我们只需要求出 min{dpi−1,k−k×costi} 部分的最值即可
那么显然,在循环每个 i 时,可以更新 dpi,j 的 k 是在变化的,我们使用单调队列维护 min{dpi−1,k−k×costi},然后 O(1) 更新即可
均摊时间复杂度为 O(nm)
 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
   | #include <bits/stdc++.h> namespace solution {     typedef long long LL;     const int MAXN = 100 + 5;     const int MAXM = 2e5 + 5;     int n, m;     struct Node     {         int start, len, cost;     };     bool operator<(const Node &a, const Node &b)     {         return a.start < b.start;     }     Node node[MAXN];     int dp[MAXN][MAXM];     int main()     {         scanf("%d%d", &m, &n);         for (int i = 1; i <= n; i++)         {             scanf("%d%d%d", &node[i].len, &node[i].cost, &node[i].start);         }         std::sort(node + 1, node + n + 1);         for (int i = 1; i <= n; i++)         {             std::deque<std::pair<int, int>> q;             q.push_back(std::make_pair(0, 0));             for (int j = 1; j <= m; j++)             {                 dp[i][j] = std::max(dp[i - 1][j], dp[i][j - 1]);                 while (!q.empty() && q.front().second < j - node[i].len)                 {                     q.pop_front();                 }                 if (!q.empty() && node[i].start <= j && j <= node[i].start + node[i].len - 1)                 {                     dp[i][j] = std::max(dp[i][j], q.front().first + j * node[i].cost);                 }                 if (j < node[i].start)                 {                     while (!q.empty() && q.back().first <= dp[i - 1][j] - node[i].cost * j)                     {                         q.pop_back();                     }                     q.push_back(std::make_pair(dp[i - 1][j] - node[i].cost * j, j));                 }             }         }         printf("%d\n", dp[n][m]);         return 0;     } } int main() {     solution::main();     return 0; }
   |