mu-oi-team 2025/5/3集训(下午)

终于开始将讲\(dp\)了!!!!!!!!!!!

前言:

主播主播,你的\(\texttt{DFS}\)确实很强,但还是太吃操作了,有没有什么更快的算法推荐一下?
有的有的,兄弟,像这么强的算法当然不止这一种了,一共有\(8\)种:
区间,树形,数位,状压,背包,排列,博弈,插头(只讲前\(6\)种)
正式开始:

1.排列:

顾名思义,为排列发明的\(dp\).
排列指\(1\sim n\)中每个数只出现1次的数列.
_____________________
关键词,要保证这一条件
\(1\sim n\)一共有\(n!\)中排列
if (要使用排列\(dp\))
题目会询问:\(n!\)中排列中,有多少个满足条件? (特征:\(1\).排列.\(2\).条件)
问:\(1\sim n\)\(n\)个数的排列,有多少个排列有偶数个逆序对?
本题用\(\texttt{DFS}\)\(T\)飞,用暴力会让你电脑卡炸,所以使用排列\(dp\).
关键词:插入.
从小到大/从大到小,把1i/in插入f[i].
变化量:逆序对数量,方案数量.
f[i][j]
\(i\):插入了\(1\sim i\)
\(j\):有\(j\)个逆序对
可以在任何地方插入数.
方程:f[i][j] = f[i+1][j+i-k]
(排列1代码)
但我们发现,这\(O(n^4)\)还是太慢了,有没有更快的方法呢?
有的兄弟,有的。我们可以只存储逆序对个数的奇偶性.
(排列2代码)
例题2:求满足条件的排列有多少个.
还是那个词:排列!
例如:条件为:有k个数比前面的数都大
直觉告诉我们,状态是这个:

f[i][j] = k;//插入到i,有j个满足条件,共有k种方案.

罢忑(But)!有一些是不满足条件的,会导致我们的代码炸掉,所以我们要重新找状态转移方程.
我们发现,本题需要从小到大插入.所以:

f[i][j] = k;//插入了i~n,有j个数满足条件,有k种方案 

这样就完美解决了爆炸的问题
所以:

f[i-1][j] += f[i][j];
f[i-1][j] += f[i][j]*(n-i+1);

分2种情况讨论
例题:
vjudge codechef LEMOVIE

区间dp:重点:区间

前2个维度:f[l][r],代表l到r的区间。
if (操作对2个相邻的元素进行)
大概率是区间dp
f[l][r] = k;:把\(a_{l\sim r}\)合并成\(1\)堆需要付出k的代价
f[i][i] = 0;:把\(1\)堆合并成\(1\)堆没有代价
f[l][r] = min(f[l][r]+f[k+1][r]+sum(l\sim r));最终代码
结束~(没错,就这么简单)

回文子序列个数:
第二类区间\(dp\),状态不变,转移要换
查看:

if 包含$s_l$但不包含$s_r$,then f[l][r] += f[l][r-1];
不包含$s_l$? 大胆! f[l][r] += f[l+1][r];
2个都不包含?大胆!f[l][r] -= f[l+1][r-1]; 
2个都包含? f[l][r] += (s[l] == s[r])*(f[l+1][r-1]+1)
综合起来,
f[l][r] = f[l][r]+f[l][r-1]+f[l+1][r]-f[l+1][r-1]+(s[l] == s[r])*(f[l+1][r-1]+1);

结束!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

posted on 2025-05-03 17:23  穆昕雨  阅读(25)  评论(0)    收藏  举报

导航