• 某 CF dp 题单


    某 CF dp 题单

    前言

    按照难度排序之后顺着来的

    诶诶 是升序不是降序了

    有的感觉不懂的或者太难的就跳过了...QAQ 蒟蒻本蒻

    尽量的做到坚持更新吧


    题目

    1. CF213C Relay Race

    一来一去太麻烦了 不妨设为两条路线一起从起点移向终点

    首先可以写出一个很显然的四维 (dp) 直接表示两条线的位置

    再看一下数据范围 (1 leq n leq 300)

    嗯 好 空间起飞了

    然后考虑怎样精简状态 上面的状态表示的是两个点的横纵坐标 这两个点一定是同时移动的 也就是移动的步数一定是相同的 考虑利用这一点 对于一个点 如果知道已经移动了多少步并且知道横纵坐标中的一个 就可以求得另一个

    所以不妨将状态设为三维

    (f_{i, j, k}) 表示移动了 (i) 步后 第一条路线所在的横坐标为 (j) 第二条线所在的横坐标为 (k) 时的最大收益

    转移考虑移动的方向以及两个点是否重复 可以比较简单的写出转移方程


    最后的答案为 (f_{2n - 1, n, n}) 一开始没想清楚 直接上的 (2n) 然后 (WA) 一片

    代码


    2. CF119C Education Reform

    不喜欢这道题 一堆限制还要打印路径

    写了一个很假的 (dp) 样例都过不了...

    然后就跑去看题解了


    其实一开始的时候在犹豫那个 (c) 有什么用 题面看不太懂 后来发现是用来排序的

    记录学科的编号 把相应的信息都记下来 然后按照 (c) 排序

    状态: 设 (f_{i, j, k}) 表示选了 (i) 门学科 到第 (j) 天 最后一门学科作业量为 (k) 时的最大作业量

    看一下数据 (a_i, b_i leq 10^{16}) 空间爆炸

    但是题目中给了 (b_i - a_i leq 100)

    这就行了

    状态: 设 (f_{i, j, k}) 表示选了 (i) 门学科 到第 (j) 天 最后一门学科作业量为 (a_i + k) 时的最大作业量

    这样空间就能过了

    转移考虑这一天的作业量是否合法 与上一天转移过来的取较大值 (还是晕...

    每进行一次转移的时候记录转移路径 最后递归输出


    注意 (c) 有相等的情况 转移的时候应该判掉

    还要开 (long long)

    话说每天的作业最大化是要闹哪样啊

    代码


    3. CF353D Queue

    其实做的时候一直感觉这是个贪...


    每个女生最少一定要换前面男生个数次 如果前面有女生 最少要换前面女生个数加一次 对于每一个 两种情况取较大即可

    为什么最少是女生个数加一次

    考虑前面的女生是否相邻

    不相邻的话 两者之间必然隔了至少一个男生

    相邻的话 会被挡住


    代码


    4. CF837D Round Subset

    总会遇到一些似鬼似仙的题目 很诡异的一个 (dp)


    首先要想到只有非零数末尾的零是有贡献的 而这些零一定来自于 (2)(5) 也就是说可以对于所取的数含有的因子 (2)(5) 的个数进行 (dp)

    然而但是我并没有想到 甚至还想对末尾的零进行 dp 直到后来发现数据跟值域沾点边就飞升了

    (dp) 的前两维是比较容易确定的

    (f_{i, j}) 表示考虑到第 (i) 个数 选取了 (j)

    考虑对所选数中含因子 (2)(5) 的个数进行 (dp)

    (f_{i, j, a, b}) 表示考虑到第 (i) 个数 选取了 (j) 个数 所选数中共包含 (a) 个因子 (2) 以及 (b) 个因子 (5) 这一状态是否可达

    答案就是 (max(min(a, b)))

    不用算复杂度就可以预感到四维的状态基本是要上天的 但是 (dp) 数组本身并没有用 考虑把其中一维塞到数组里面

    (f_{i, j, k}) 表示考虑到第 (i) 个数 选取了 (j) 个数 所选数中共包含 (k) 个因子 (2) 时包含因子 (5) 的数量

    转移考虑每个数选与不选 当成背包就可以了


    注意数组第三维的大小 大概开到 (6000) 左右 因子的个数

    第一维和第二维都是 (200) 的 感觉空间还是有点紧张

    第一维 (i) 的转移只与 (i - 1) 有关 可以滚一下

    注意开 (long long)

    代码


    5. CF796D Police Stations

    一个混入 (dp) 题单的广搜题...

    看到题解里面好多人都说 (d) 没有用... 输入保证了树合法 那个 (d) 好像确实没有用


    直接将警局入队 然后开始搜就可以了 对搜到的点染色 一条边的两边的点的颜色不同 那这条边就是可以删掉的


    代码


    6. CF540D Bad Luck Island

    感觉又是一个不是很难但是比较鬼畜的题目


    三个数的比较小 状态直接设三维没有问题

    (f_{i, j, k}) 表示三个种族剩余的人数分为为 (i, j, k) 个时的概率

    (f_{r, s, p}) 自然是 (1)

    (i imes j + j imes k + k imes i) 是所有情况发生的概率 转移考虑某一情况的发生


    代码


    7. CF527D Clique Problem

    一道神仙构造题


    对于两个点 (i, j) 不妨设 (x_i < x_j) 对于两点之间右边的条件即为 (x_j - x_j > w_i + w_j)(i) 放到一边 有:$$x_i - w_i > x_j + w_j$$

    对于一个点覆盖的区间 为 ([x_i - w_i, x_i + w_i]) 设为 ([l_i, r_i]) 上式可以化为 $$l_i > r_j$$ 相当于两个区间没有交点

    如果将所有的点转化为线段 那么所有没有交集的线段之间都是有边的 所以问题相当于在数轴上取若干条线段 使得所取的所有线段没有交集 最大化所取线段的数量

    比较显然的贪心 按照线段右端点排序 直接取即可


    代码


    8. CF478D Red-Green Towers

    首先可以很自然的写出一个三维的 (dp)

    (f_{i, j, k}) 表示搭 (i) 层 使用 (j) 个红块 (k) 个绿块的方案数

    有了层数自然可以算出所用的总方块数量 显然第三维是没有必要的

    状态 : (f_{i, j}) 表示搭 (i) 层 使用 (j) 个红块的方案数

    转移 : (f_{i, j} = f_{i - 1, j} + f_{i, j - i})

    考虑一下时空复杂度 积木最多有 (4 imes 10^5) 块 若是搭 (h) 层 有: $$frac {h imes (h + 1)}2 = r + g$$ 估一下大概九百到一千

    感觉时间比较玄乎 开了两秒好像也差不多 空间的话 显然 (f_i) 的转移只与 (f_{i - 1}) 有关 可以直接滚掉一维

    然后考虑答案的区间

    (dp) 数组的第二维已经限制了 (r) 是合法的 所以需要考虑 (g) 是否足够

    考虑只用 (g) 有 $$frac {h imes (h + 1)}2 = g$$ 也就是说 (r) 最少需要使用 (frac {h imes (h + 1)}2 - g) 块 在这个范围内统计答案即可


    代码


    9. CF505C Mr. Kitayuta, the Treasure Hunter

    边界比较苟的一道 (dp)


    很自然的一个状态是 (f_{i, j}) 表示当前在 (i) 上一步跳了 (j) 时的宝藏数

    看一下数据范围 都在 (3 imes 10^4) 数量级 空间炸了

    (空间: 我tm天天爆炸)

    但是题目中给出每次跳的距离与上一次的浮动为 (c - 1, c, c + 1) 假设每一步都与上一步发生浮动 一共跳跃了 (x) 步 有 $$frac {d imes (d + 2x)}2 = n$$

    (d = 1, n = 3 imes 10^4) 算出的 (x) 大概为三百左右 但是不到

    这样我们可以将状态改为

    (f_{i, j}) 表示当前在 (i) 位置 上一步跳了 (d + j) 时的宝藏数

    这样空间就没有问题了

    但是很明显 (j) 是有负数的 所以需要全部加上一个常数 调整下标

    转移考虑上一次的位置及跳跃距离即可


    代码


    10. CF547B Mike and Feet

    混入 (dp) 题单的单调栈


    对于每一长度的区间 最大化区间中的最小值

    在一段区间中的最小值是固定的 设为 (a_i) 将这种极大区间称为 (a_i) 的控制范围

    显然若 (a_i) 控制的区间为 ([l_i, r_i]) 那么对于所有区间长度为 ([1, r_i - l_i + 1]) 的区间都可以做出贡献

    对每一个数处理出其控制的区间 贡献到其所能贡献的最大的区间长度上 对于贡献到同一位置的答案取最大值 再从 (n) 逆向推回来 对于每一长度的区间 在能取的答案中取最大值即可


    代码


    11. CF1271D Portals

    好题

    做法非常多的一道题


    首先需要明确一点 对于一个城堡 一定是在能占领它的最晚的时间使用 (1) 的兵力进行占领是最优的

    正确性也很显然 任意时间占领的贡献是一样的 早占领还有可能导致后面的兵力不足

    所以对于每个点只需要保留其最后能够占领的时间即可


    解法 (1)

    很自然的一个 (dp) 状态:

    (f_{i, j}) 表示当前在 (i) 个城堡 剩余 (j) 个士兵时的最大收益

    数据范围最大是 (5000) 完全没有问题

    转移:

    在任意时刻 $$f_{i, j} = max(f_{i - 1, j - b_i})$$

    如果当前为某一城堡最后可以占领的时刻 $$f_{i, j} = max(f_{i, j + 1})$$

    转移的格式比较诡异 统一一下 刷表

    [f_{i, j + b_i} = max(f_{i - 1, j}) ]

    [f_{i, j} = max(f_{i, j + 1}) ]

    答案为 (max(f_{n, i}))

    复杂度 (O(n^2))


    代码


    解法 (2)

    总兵力以及每个城堡需要和补充的兵力都是有的 不妨将所有的城堡都打下来 再考虑占领

    首先在打的过程中兵力不够了 自然是不合法的 因为现在还没有考虑占领的问题

    都打完之后呢

    我们可以从大到小的贪心的占领城堡

    为什么

    因为一个城堡只需要 (1) 的兵力 不存在空下某一较大的收益而得到其他更大收益的情况

    反正花费都是一样的 我为什么不占收益大的

    所以直接贪心的考虑每一座城堡

    考虑在某一时刻占领每一座城堡 占领的时刻一定是这个城堡可以占领的最后时刻 在这一时刻占领一座城堡就会影响后面的操作 相当于在之后攻打所有城堡时所能使用的兵力减一

    我们可以记录一个数组 为攻打该城堡的余俗兵力 对某一时刻 (t)(t) 时刻到 (n) 时刻的余俗兵力的最小值大于 (0) 时 这一座城堡是可以占领的 将这一城堡的贡献累加下来 对于 (t)(n) 时刻的余俗兵力减一

    区间加 区间最小值 线段树可以直接维护

    复杂度 (O(nlog n))


    代码


    解法 (3)

    可以直接贪吗?

    对于每一座城堡 在最后的时间之间占下来 到兵不够的时候再说

    那真到了兵不够的时候 怎么办

    找一个收益最少的地方 把这里吐出来 把兵腾出来继续打

    为什么

    首先要能打下来 所以当然可以吐 占哪不是占 花费都一样 为啥不占大的 把小的空出来

    维护一个最小值 可以通过优先队列实现 把占领的点扔进去 吐的时候取最小的吐就行了

    不合法的状态自然就是把地吐干净了依旧不够 或者最后打完了 兵成了负的 还得吐


    代码


    上面那个解法三相当于为贪心提供了一个反悔的机会 也是属于一类问题 反悔贪心

    有关的题目

    CF865D Buy Low Sell High

    P1792 [国家集训队]种树


  • 相关阅读:
    Javascript笔记部分
    JQuery学习笔记(3)
    WPF中批量进行验证操作
    学习sqlserver的函数方法
    .NET学习笔记(2)
    Asp.Net页面生命周期
    ThreadLocal
    NIO内存映射
    CAS基础和原子类
    Java锁概念基础
  • 原文地址:https://www.cnblogs.com/blank-space-/p/15096712.html
Copyright © 2020-2023  润新知