各种链接

动态规划pdf:https://files.cnblogs.com/files/loadingkkk/示例.zip?t=1688734522&download=true

cherry-markdown生成的文件:https://files.cnblogs.com/files/loadingkkk/课程报告规划.zip?t=1688742221&download=true

动态规划


张开泰

斜率优化到底是个啥东西?

例题:

你有 n 名士兵,从 1 到 n 编号,你要将他们拆分成若干特别行动队调入战场。出于默契的考虑,同一支特别行动队中队员的编号应该连续,即为形如 \((i, i + 1, \cdots i + k)\)的序列。所有的队员都应该属于且仅属于一支特别行动队。

编号为 i 的士兵的初始战斗力为 \(x_i\) ,一支特别行动队的初始战斗力 X 为队内士兵初始战斗力之和,即 \(X = x_i + x_{i+1} + \cdots + x_{i+k}\)

对于一支初始战斗力为 X的特别行动队,其修正战斗力 \(X'= aX^2+bX+c\),其中 \(a,b,c\)是已知的系数(\(a < 0\))。现在你要为这支部队进行编队,使得所有特别行动队的修正战斗力之和最大。试求出这个最大和。

\(0\le n \le10^6,-5\le a \le-1,-10^7\le b,c \le10^7,1\le x_i \le100\)

朴素(暴力)算法:枚举序列的划分方案,指数级别

朴素的dp:

\(f(i)\)为划分前\(i\)个士兵所得的战斗力之和最大是多少

\(sum(i)\)\(x_i\)的前缀和数组。

\(g(x)=ax^2+bx+c\)

\(f(i)=max\{f(j)+g(sum(i)-sum(j))|0\le j\le i\}\)

时间复杂度\(O(n^2)\)

斜率优化dp:

重要思想:利用数学变换的方式,对dp方程进行优化,一般是把下标相同的归为一类,如果这是能够实现的,那么就很有机会进行优化(不止是斜率优化)。

对于斜率优化,需要有找到变量之间的关系,设法构造一次函数(\(y=kx+b\)),利用一次函数之间的关系作图求解。

\(f(i)=max\{f(j)+a(sum(i)-sum(j))^2+b(sum(i)-sum(j))+c\}\)

\(f(i)=max\{f(j)+a*sum^2(i)+a*sum^2(j)-2a*sum(i)sum(j)+b*sum(i)-b*sum(j)+c\}\)

\(f(i)=max\{f(j)+a*sum^2(j)-2a*sum(i)sum(j)-b*sum(j)\}+a*sum^2(i)+b*sum(i)+c\)

对于\(Y=KX+B\)

\(K_j=-2a*sum(j)(>0)\)

\(X_i=sum(i)(>0)\)

\(B_j=f(j)+a*sum^2(j)-b*sum(j)\)

原问题转化为找到最合适的\(j\),使得\(Y_i=K_jX_i+B_j\)取得最大值

此值一定再许多一次函数构成的凸包上。

求出\(f(i)\)把对应的一次函数(\(Y=K_iX+B_i\))加入凸包(此时\(i\)的一次函数斜率再所有一次函数中是最大的,一定是能够加入凸包的)

用栈储存凸包上的一次函数,加入新的一次函数时需要判断是否需要弹栈。

image-20230315222949718

关于dp的想法

dp是解决问题的一个很好的工具,dp思想在资源的划分与利用方面发挥很大的作用。

dp的状态设计:尽可能避免冗余状态,尽可能用少的资源储存多的信息,尽可能储存关键信息

dp的转移方程:避免冗余,无后效性。

一个合法的dp的状态和转移构成了一个有向无环图(DAG),状态即为图中的点,转移为图中的有向边。

优化dp也是从状态和转移入手,减少冗余状态,或者优化转移,使转移变得有序或有规律。

支线1 常见dp状态定义及转移汇总

简单型:

线性dp,背包dp

树dp

前置芝士:dfs,树

例题 洛谷 P2014 CTSC1997 选课

现在有\(n\)门课程,第\(i\)门课程的学分为\(a_i\)每门课程有零门或一门先修课,有先修课的课程需要先学完其先修课,才能学习该课程。

一位学生要学习\(m\)门课程,求其能获得的最多学分数。

每门课最多只有一门先修课的特点,与有根树中一个点最多只有一个父亲结点的特点类似,所以可以建树处理。

所有课程组成了一个森林的结构。为了方便起见,我们可以新增一门0学分的课程(设这个课程的编号为0),作为所有无先修课课程的先修课,从而把森林变成一棵树。

dp设计: \(设f(u,i,j)表示以u为根的子树中,已经遍历了u号点的前i棵子树,选了j门课程的最大学分\)

转移的过程结合了树形DP和背包dp的特点,我们枚举\(u\)点的每个子结点\(v\),同时枚举以\(v\)为根的子树选了几门课程,将子树的结果合并到\(u\)上。

\(f(u,i,j)=max\{f(u,i-1,j-k)+f(u,s_v,k)\}\)

其中\(s_v\)为v的儿子数。

注意上面状态转移方程中的几个限制条件,这些限制条件确保了一些无意义的状态不会被访问到。

\(f\)数组的第二维在转移过程中可以利用滚动数组或的方式,或背包dp常用技巧省略。

时间复杂度\(O(nm)\)

int dfs(int u) {
  int p = 1;
  f[u][1] = s[u]; 
    for (auto v : e[u]) {//枚举每个儿子
        int siz = dfs(v);//siz储存子树大小
        // 注意下面两重循环的上界和下界
        // 只考虑已经合并过的子树,以及选的课程数超过 m+1 的状态没有意义
        for (int i = min(p, m + 1); i; i--)
          for (int j = 1; j <= siz && i + j <= m + 1; j++)
            f[u][i + j] = max(f[u][i + j], f[u][i] + f[v][j]);// 转移方程
        p += siz;
      }
	return p;
}

区间dp

区间类动态规划是线性动态规划的扩展,它在分阶段地划分问题时,与阶段中元素出现的顺序和由前一阶段的哪些元素合并而来有很大的关系。

令状态\(f(i,j)\)表示将下标位置\(i\)\(j\)的所有元素合并能获得的价值的最大值,那么\(f(i,j)=max\{f(i,k)+f(k+1,j)+cost\}\),\(cost\)为将这两组元素合并起来的代价。

区间 DP 有以下特点:

合并:即将两个或多个部分进行整合,当然也可以反过来;

特征:能将问题分解为能两两合并的形式;

求解:对整个问题设最优值,枚举合并点,将问题分解为左右两个部分,最后合并两个部分的最优值得到原问题的最优值。

计算\(f(i,j)\)时通常以\(len=j-i+1\),即区间长度作为阶段进行转移。

「NOI1995」石子合并

题目大意:在一个环上有\(n\)堆石子,要进行\(n-1\)次合并操作,每次操作将相邻的两堆石子合并成一堆,能获得新的一堆中的石子数量的和的得分。你需要最大化你的得分。

弱化版:不是在环上,而是在链上。

\(f(i,j)\)为将[i,j]内的石子合并到一起的最大得分

\(f(i,j)=max\{f(i,k)+f(k+1,j)+sum_j-sum_{i-1}\}\)

从小到大枚举区间长度,第二层枚举该长度下的所有区间,第三层枚举区间的断点,复杂度\(O(n^3)\)

处理环:断环成链,复制一倍接在原序列之后即可。

for (len = 1; len <= n; len++)
  for (i = 1; i <= 2 * n - 1; i++) {
    int j = len + i - 1;
    for (k = i; k < j && k <= 2 * n - 1; k++)
      f[i][j] = max(f[i][j], f[i][k] + f[k + 1][j] + sum[j] - sum[i - 1]);
  }

时间复杂度O(n^3);

某个多边形划分的作业题也可以用这种方法处理。

坐标dp

形象地理解为多了一维的线性dp

进阶型:

数位dp,状态压缩dp,插头dp

https://oiwiki.org/

支线2 树上背包进阶

有一棵点数为 n 的树,树边有边权。给你一个在 \(0 \sim n\)之内的正整数 \(k\) ,你要在这棵树中选择 \(k\) 个点,将其染成黑色,并将其他 的 \(n-k\) 个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的收益。问收益最大值是多少。

\(0\le n,k\le2000\)

暴力:略

暴力dp:

直观的状态定义:\(f(i,j)\)表示以i为根的子树染了j个黑色点的最大收益(仅限子树内,子树外的点不做考虑)。

然后转移?

\(f(i,j)=f(i,j-o)+f(ch,o)+?\)

树上的dp一般是父亲与儿子之间进行转移,考虑父子的信息是如何传递的

如何有效地利用信息?

考虑将收益下放到每一条边

对于一条边\(e\),删掉\(e\)之后,整颗树分裂成两棵树,这条边被统计到的次数为两颗树黑点个数之积与白点个数之积的和。

对于一棵子树,若确定子树内部的黑点的个数,那也同时确定了子树内部白点,子树外黑白点的个数。

状态定义的转化

\(f(i,j)\)表示以i为根的子树染了j个黑色点的最大收益(按照边进行统计,统计子树外的点对子树内的边(对答案)的贡献)。

\(size(i)\)表示以i为根的子树的大小

问题转化:求每个父子边的贡献,

\(f(i,j)=max\{f(i,j-o)+f(ch,o)+o*(K-o)+(size(ch)-o)*(n-size(o)-(K-o))|0\le j\le min(K,size(i)),0 \le o\le j\}\)

转移过程:枚举o,类似背包进行转移,把儿子的信息传递给父亲。

posted @ 2023-04-28 12:35  真-不能AKt♞  阅读(44)  评论(0)    收藏  举报