「单调优化 dp」做题记录

「单调优化 dp」做题记录

  1. P1941 [NOIP2014 提高组] 飞扬的小鸟

    \(f(i, j)\) 表示使小鸟到达 \((i, j)\) 所需的最少点击数。

    不难写出转移方程:

    \[f(i, j) = \min \begin{cases} f(i-1, j + y_{i-1}), \text{if} j + y_{i-1} \le m \\ f(i-1, x - kx_{i-1}), k \in \mathbb{N}^+ \and j - kx_{i-1} \ge 1 \end{cases} \]

    其中第一种情况对应在上一点不点击,第二种情况对应在上一点点击若干次(\(k\) 次)。由于小鸟高度为 \(m\) 时,无法再上升,所以 \(j = m\) 的情况要特殊处理。

    初始化:

    \[f(i, j) = \begin{cases} 0, i = 0 \\ +\infty, \text{Otherwise} \end{cases} \]

    如果 \(\forall 1 \le j \le m\)\(f(n, m) = +\infty\),则无法到达终点,否则到达终点的最小点击数为 \(\min_{1 \le j \le m} f(n, j)\)

    容易发现,由于要枚举 \(k\),每次转移的时间复杂度大于 \(O(1)\)。虽然在开启 O2 的情况下能通过此题,但我们仍需更好的转移方程。

    但是我还没搞懂优化,洛谷题解写的都是什么东西?还有这不是单调优化题单里的题吗,我怎么没看出单调优化,题解里都在说完全背包的事😅剩下的转移方程,以后再来探索吧

  2. P3089 [USACO13NOV] Pogo-Cow S

    不失一般性,假设 Bessie 从左往右跳。并且先把所有目标点按坐标从小到大排序。

    \(f(i, j)\) 表示 Bessie 在由第 \(j\) 个目标点跳到第 \(i\) 个目标点的情况下,能获得的最大总分数。设 \(i\)\(j\) 之间的距离 \(x_i - x_j = dis\),则转移方程为

    \[f(i, j) = \max_{k < j \and x_j - x_k \le dis}f(k, j) + val_i \]

    初始化 \(\forall 1 \le i \le n, f(i, 0) = val_i\),代表从第 \(i\) 个目标点开始跳跃;其他情况下 \(f(i, j) = -\infty\)

    答案为 \(\max f(i, j)\)

  3. P2627 [USACO11OPEN] Mowing the Lawn G

    \(f(i, 1/0)\) 表示在选/不选第 \(i\) 只奶牛的情况下,前 \(i\) 只奶牛能获得的最大总收益。

    则转移方程为

    \[f(i, 1) = \max_{i-K+1 \le j < i} (\sum_{k=j}^{i} E_k + f(j-1, 0)) \\ f(i, 0) = \max(f(i-1, 0), f(i-1, 1)) \]

    时间复杂度为 \(O(NK)\),无法通过此题。

    由于 \(f(i, 1)\) 的转移方程中出现了某一连续段的最大值,考虑单调优化。

    这里的单调优化略微有点说法: \(\sum_{k=j}^{i} E_k\) 这一项是随着 \(i\) 的变化而动态更新的,因此单调队列中不能对于每个 \(j\) 维护 \(\sum_{k=j}^{i} E_k + f(j-1, 0)\)

    利用前缀和:\(\sum_{k=j}^{i} E_k = sum_i - sum_{j-1}\),则原转移方程变为 :

    \[f(i, 1) = \max_{i-K+1 \le j < i} (f(j-1, 0) - sum_{j-1}) + sum_i \]

    于是就可以在单调队列中维护 \(f(j-1, 0) - sum_{j-1}\)

    AC 记录

  4. P2254 [NOI2005] 瑰丽华尔兹

    先想一个 \(O(NMT)\) 的做法:设 \(f(t, i, j)\) 表示 \(t\) 时刻在 \((i, j)\) 的情况下,已经过的最长滑行距离。

    则转移方程为:

    \[f(t, i, j) = \max(f(t-1, i, j), f(t-1, si, sj) + 1) \]

    其中 \((si, sj)\) 表示上一时刻钢琴的位置,这是由上一时刻的风向决定的。

    初始化:若 \((i, j)\) 是空地,则 \(f(1, i, j) = 0\),否则 \(f(1, i, j) = -\infty\)

    提交记录(50 pts)

    由于 \(T\) 最大可以达到 \(40000\),而时间复杂度中的 \(NM\) 难以去掉,所以考虑去掉 \(T\)

    重新设计状态:设 \(f(x, i, j)\) 表示第 \(x\) 个区间结束时(即在 \(t_x + 1\) 时刻),钢琴在 \((i, j)\) 的情况下,已经过的最长滑行距离。

    则转移方程为:

    \[f(x, i, j) = \max_{0 \le d \le t_x - s_x + 1} (f(x-1, si, sj) + d) \]

    这里,我们枚举 \(d\) 表示在第 \(x\) 个区间中钢琴滑行的距离,\((si, sj)\) 表示滑行前所在的位置。

    由于转移方程中出现了某一连续段的最大值,我们可以使用单调优化。

    由于 \(d\) 是变化量,我们把他拆开: \(d = j - sj\) (这里以向左移动为例),把 \(j\) 提出来,\(sj\) 就变成了常量,这样就可以单调优化。

    这里代码实现上需要一些小技巧,否则会使代码很冗长。对于不同的移动方向,我们计算两个点距离的方式也不同。在代码中,可以把这个过程抽象成一个函数,用某个变量 i 表示当前格是所在行/列的第 i 个格,这样就可以完成不同方向的统一。详见代码(代码中使用了滚动数组):

    void work(int x, int y, int d, int len)
    {
    	head = 1, rear = 0;
    	for(int i = 1; check(x, y); i++, x += dx[d], y += dy[d])
    	{
    		if(mp[x][y] == 'x') head = 1, rear = 0;
    		else
    		{
    			while(head <= rear && f[x][y]-i >= que[rear].val - que[rear].pos) rear--;
    			que[++rear] = Node(f[x][y], i);
    			while(head <= rear && que[head].pos < i - len) head++;
    			f[x][y] = que[head].val + (i - que[head].pos);
    			ans = max(ans, f[x][y]);
    		}
    	}
    }
    

    主函数中:

    memset(f, 0xc0, sizeof(f));
    f[sx][sy] = 0;
    for(int k = 1, s, t, d; k <= K; k++)
    {
        cin >> s >> t >> d;
        int len = t - s + 1;
        if(d == 1) for(int i = 1; i <= m; i++) work(n, i, 1, len);
        else if(d == 2) for(int i = 1; i <= m; i++) work(1, i, 2, len);
        else if(d == 3) for(int i = 1; i <= n; i++) work(i, m, 3, len);
        else if(d == 4) for(int i = 1; i <= n; i++) work(i, 1, 4, len);
    }
    

    AC 记录

  5. P1725 琪露诺

    \(f(i)\) 表示到达第 \(i\) 格时琪露诺能获得的最大冰冻指数。

    转移方程(省略 \(0 \le j \le N\) 等边界条件):

    \[f(i) = \max_{i-R \le j \le i - L} f(j) + A_i \]

    初始化:\(f(0) = 0\),其他情况 \(f(i) = -\infty\)

    答案统计:\(ans = \max_{N+1 \le i \le N+R} f(i)\)

    很难不想到单调优化,很难不会实现单调优化。

posted @ 2024-07-29 19:42  DengStar  阅读(20)  评论(0)    收藏  举报