【DP做题记录】
插数dp
(名字是我口胡的,因为它直观上看就在做插数这件事)
假设有一个给定的排列,初始我们有一个空序列,现在从小到大往这个空序列中插入1~n,使得最后得到的序列恰为给定的排列
在插入的过程中我们可以把当前得到的序列想象成若干段,每一段就表示这一段是一个连续的整体,以后插数不能在往里插了;同时我们钦定,相邻的两段若要合并,则必须在接下来的插数过程中往这两段之间插入至少一个数
最后得到的长度为n且恰有一段的序列即为给定的排列。
显然任何一个排列都可以通过如上方式生成,且生成的方式是唯一的,即对于给定的排列,每次插入一个新的数时,我们插入这个数的位置和这个数是否与两边的段进行合并是确定的。也就是说通过这种方式,我们可以不重不漏的统计所有的排列,这便是接下来dp的基础。
Kangroo
首先我们可以把题目抽象成:统计有多少个排列满足开头为s,结尾为t,且对于任意一个位置,其左边和右边的值要么都大于它,要么都小于它
我们考虑通过刚才插数的过程把所有排列构造出来,设f[i][j]表示已经插入1~i,形成了j个段的方案数。考虑转移:
先不考虑s,t,假设现在已经插入前i-1个数,一共有j段,要插入第i个数。有两种情况:
- 在j+1个空中挑一个插进去形成新的一段
- 在j-1个被两段相夹的空中插入并把两段合并在一起,形成一个新段。
你会发现,这其中似乎有遗漏的情况:比如为什么不能插进去后只和相邻的一段进行合并
因为我们是从小到大插入数,可以通过归纳证明,任意时刻一段中最边上的元素一定是个“谷”(小于它两边的元素),因此插入的这个数大于之前插入的任何数,正好可以作为“峰”(大于它两边的元素)将两段合并,如果只和其中的一段进行合并,那么之后插入的元素只会比他大,不能再将它再次合并。所以这种情况是不合法的
对于s,t,用刚才的思路也不难推理出来。
CF704B Ant Man
可以用插数dp来做,但是挖掘题目性质,似乎有的贪心做法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?