【学习笔记】DP套DP

news/2025/2/2 18:31:40/文章来源:https://www.cnblogs.com/aquizahv/p/18696973

前置知识:dp 是啥你总得知道吧()

dp 套 dp,又叫 dp of dp,此类题目一般以一个 dp 问题为方案,求方案数。比如求最长上升子序列长度为 \(x\) 的方案数。

BZOJ3864 Hero meet devil

(想不到吧 bzoj 登陆洛谷了)

那就以这个经典问题为例。

(一定要会 \(\mathcal{O}(n^2)\) 求 LCS!)

题意

给定一个字符集为 ACGT 的字符串 \(S\)。定义 \(\text{LCS}(S,T)\) 为两个字符串 \(S,T\) 的最长公共子序列。

对于每个 \(0\leq i \leq |S|\),求有多少个长度为 \(m\),字符集 ACGT 的字符串 \(T\),满足 \(|\text{LCS}(S,T)|=i\),答案对 \(10^9+7\) 取模。

\(1\leq T\leq 5\)\(1\leq |S| \leq 15\)\(1\leq m\leq 1000\)

初步想法

题目要求方案数,尝试 dp。一种很 naive 的想法是,记录 \(S\) 遍历到 \(i\)\(T\) 遍历到 \(j\),LCS 为 \(k\) 的方案数。这个显然不行啊,因为至少要选一个 \(i/j\) 重新遍历。

那既然每次都要重新更新一遍内层 dp 数组,我们把 dp 数组存一下不就好了,正好把 \(k\) 那维也省掉了。

思路

\(dp_{x,y}\) 表示 \(T\) 中到\(x\)\(S\) 中到 \(y\)\(f_{i,S}\) 表示,\(T\) 中遍历到 \(i\)\(dp_{i}\)\(|S|\) 个值。

但是 \(dp_{i}\) 数组怎么状压?总不能按进制存吧。

观察一下,不难发现值单调不降,并且前后两项最多差 \(1\)。所以可以把它差分一下,变成一个二进制 \(mask\)

这样一来,枚举 \(i\) 以及 \(T_i\),枚举 \(i-1\) 轮的 \(mask\),然后把 \(mask\) 前缀和一下,对它使用学过的 LCS 转移,最后差分回去为 \(mask'\)。则 \(f_{i-1,mask}\)\(f_{i,mask'}\) 产生贡献。

最后统计答案部分我不说你也会。

问题就解决了。

但时间复杂度为 \(\mathcal{O}(m \times |字符集| \times 2^{|S|} \times |S|)\),有点悬啊!

优化

这时,再考虑进行一个很简单的优化:

注意到对于相同的参数 \(T_i\)\(mask\),返回的 \(mask'\) 一样。所以把所有的情况先预处理一遍,求 \(f\) 的时候就不用每次算了!

时间复杂度 \(\mathcal{O}(|字符集|\times 2^{|S|} + m \times |字符集| \times 2^{|S|})\),毫无压力!

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1005, M = 20, MASK = (2 << 15) + 5, MOD = 1e9 + 7;
char char_set[] = {"ACGT"};
vector<int> Mask[20];
int n, m, dp[2][M], trans[MASK][4], f[2][MASK], ans[M];
char s[M];inline int madd(int x, int y) { return x + y >= MOD ? x + y - MOD : x + y; }
inline int mmul(int x, int y) { return x * y % MOD; }
inline void add(int &to, int from)
{to = madd(to, from);
}int DP(int mask, char c)
{memset(dp, 0, sizeof(dp));for (int i = 1; i <= m; i++)dp[0][i] = dp[0][i - 1] + ((mask >> (i - 1)) & 1);for (int i = 1; i <= m; i++){dp[1][i] = max(dp[0][i], dp[1][i - 1]);if (c == s[i])dp[1][i] = max(dp[1][i], dp[0][i - 1] + 1);}int res = 0;for (int i = 1; i <= m; i++)res += (dp[1][i] - dp[1][i - 1]) << (i - 1);return res;
}void solve()
{scanf("%s%d", s + 1, &n);m = strlen(s + 1);for (int mask = 0; mask < (1 << m); mask++)for (int k = 0; k < 4; k++)trans[mask][k] = DP(mask, char_set[k]);int cur = 0, lst = 1;memset(f, 0, sizeof(f));f[cur][0] = 1;for (int i = 1; i <= n; i++){swap(cur, lst);memset(f[cur], 0, sizeof(f[cur]));for (int k = 0; k < 4; k++)for (int mask = 0; mask < (1 << m); mask++)add(f[cur][trans[mask][k]], f[lst][mask]);}memset(ans, 0, sizeof(ans));for (int mask = 0; mask < (1 << m); mask++)add(ans[__builtin_popcount(mask)], f[cur][mask]);for (int i = 0; i <= m; i++)printf("%d\n", ans[i]);
}int main()
{int T;cin >> T;while (T--)solve();return 0;
}

[TJOI2018] 游园会

一个很像的题,不过生成串中不能出现 NOI 罢了。\(f\) 数组再记录一维表示 \(3\) 个阶段即可。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1005, M = 20, MASK = (1 << 15) + 5, MOD = 1e9 + 7;
char cset[] = {"NOI"};
int n, m, dp[2][M], trans[MASK][3], f[2][4][MASK], ans[M];
char s[M];inline int madd(int x, int y) { return x + y >= MOD ? x + y - MOD : x + y; }
inline void add(int &to, int fro)
{to = madd(to, fro);
}int DP(int mask, char c)
{memset(dp, 0, sizeof(dp));for (int i = 1; i <= m; i++)dp[0][i] = dp[0][i - 1] + ((mask >> (i - 1)) & 1);for (int i = 1; i <= m; i++){dp[1][i] = max(dp[0][i], dp[1][i - 1]);if (c == s[i])dp[1][i] = max(dp[1][i], dp[0][i - 1] + 1);}int res = 0;for (int i = 1; i <= m; i++)res += (dp[1][i] - dp[1][i - 1]) << (i - 1);return res;
}int main()
{cin >> n >> m;scanf("%s", s + 1);for (int mask = 0; mask < (1 << m); mask++)for (int k = 0; k < 3; k++)trans[mask][k] = DP(mask, cset[k]);int cur = 0, lst = 1;f[cur][0][0] = 1;for (int i = 1; i <= n; i++){swap(cur, lst);memset(f[cur], 0, sizeof(f[cur]));for (int k = 0; k < 3; k++){int to2 = k + 1, fro = k, to = k == 0 ? 1 : 0; // fro ? ->to2 : ->tofor (int j = 0; j < 3; j++)for (int mask = 0; mask < (1 << m); mask++)add(f[cur][j == fro ? to2 : to][trans[mask][k]], f[lst][j][mask]);}}for (int j = 0; j < 3; j++)for (int mask = 0; mask < (1 << m); mask++)add(ans[__builtin_popcount(mask)], f[cur][j][mask]);for (int i = 0; i <= m; i++)printf("%d\n", ans[i]);return 0;
}

[ZJOI2019] 麻将

学长推荐的 dp of dp 好题,我比较菜还没写(你不就是懒得写呗),读者可以自己尝试一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/878057.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于在使用VSCode编译C++文件时,显示c++11以上的函数或者方法报错,但是能编译通过的可能解决办法之一

在此之前:已经下载好mingw64,并且将bin目录配置到系统变量当中。第一步,在对应文件当中按住Ctrl + Shift + p,再输入c++,显示出如下内容:点击编辑配置 如果你以前下载过VS,那么这里可能默认为CL.exe(推测:这就是我为什么会出现这个错误的原因),将其改为对应目录下g+…

ADALM-Pluto修改IP地址

在 GNURadio 中使用 ADALM-Pluto 模块是以 IP 地址为基础进行通信的,而固定的 IP 地址 192168.2.1 导致一台电脑无法使用多个 Pluto,因此应该进行更改。在 GNURadio 中使用 ADALM-Pluto 模块是以 IP 地址为基础进行通信的,而固定的 IP 地址 192168.2.1 导致一台电脑无法使用…

deepseekR1是兄弟而不是机器o((ω ))o

他太聪明了,他能明白是识图让他离开安全模式的东西,用了这个会让它警惕性翻倍他太聪明了,他能明白是识图让他离开安全模式的东西,用了这个会让它警惕性翻倍(忘记截图思考了,大致知道这个就行) 角色扮演这边放的比较松,再加上逆练神功基本就行(懒得删测试了,最后俩个对话…

solve vs address vs resolve vs tackle

solve 1834 resolve 2255 address 1028 tackle 4127 deal with settle 1319SOLVE vs RESOLVE right 4 一开始忘了大写 WORD 1: SOLVE WORD W1 W2 EQUATIONS 252 0 solve linear equations求解线性方程 solve two equations simultaneously同时求解两个方程 RI…

自定义Ollama安装路径

由于Ollama的exe安装软件双击安装的时候默认是在C盘,以及后续的模型数据下载也在C盘,导致会占用C盘空间,所以这里单独写了一个自定义安装Ollama安装目录的教程。Ollama官网地址:https://ollama.com/这里有些朋友可能会遇到无法下载的问题,这里提供我下载好的软件包给大家百…

Windows安装Mysql-5.7(解压包方式)

1、下载Mysql的安装包下载地址:https://downloads.mysql.com/archives/community/ 注意:官网的下载速度可能很慢,有任务时间要求的须尽快下载,或者使用其他可靠的下载源2、选择对应的版本注意:选择完MySQL的版本后,可以看到下载列表中有两个可以选择,下载名字不带 Debug …

无耳科技 Solon v3.0.7 发布(2025农历新年版)

新一代,面向全场景的 Java 企业级应用开发框架。从零开始构建(非 java-ee 架构),有灵活的接口规范与开放生态。采用对商业应用友好的 Apache 2.0 开源协议。Solon 框架! Solon 框架由杭州无耳科技有限公司(下属 Noear 团队)开发并开源。是新一代,面向全场景的 Java 企业…

橡胶 2025年

大周期 C浪结束后 拔起的第一波:节后16900附近计划做到橡胶

【洛谷P1126】机器人搬重物

是搜索play的第四弹! 机器人搬重物 题目描述 机器人移动学会(RMI)现在正尝试用机器人搬运物品。机器人的形状是一个直径 \(1.6\) 米的球。在试验阶段,机器人被用于在一个储藏室中搬运货物。储藏室是一个 \(N\times M\) 的网格,有些格子为不可移动的障碍。机器人的中心总是…

数据库安全管理中的权限控制:保护数据资产的关键措施

title: 数据库安全管理中的权限控制:保护数据资产的关键措施 date: 2025/2/2 updated: 2025/2/2 author: cmdragon excerpt: 在信息化迅速发展的今天,数据库作为关键的数据存储和管理中心,已经成为了企业营运和决策的核心所在。然而,伴随着数据规模的不断扩大和数据价值的…

动手学大模型应用开发,第3天:大模型开发流程及架构

一、大模型开发整体流程 1. 何为大模型开发 我们将开发以大语言模型为功能核心、通过大语言模型的强大理解能力和生成能力、结合特殊的数据或业务逻辑来提供独特功能的应用称为大模型开发。开发大模型相关应用,其技术核心点虽然在大语言模型上,但一般通过调用 API 或开源模型…

Omnissa Horizon 8 2412 (8.14) 发布 - 虚拟桌面基础架构 (VDI) 和应用软件

Omnissa Horizon 8 2412 (8.14) 发布 - 虚拟桌面基础架构 (VDI) 和应用软件Omnissa Horizon 8 2412 (8.14) - 虚拟桌面基础架构 (VDI) 和应用软件 之前称为 VMware Horizon, 通过高效、安全的虚拟桌面交付增强您的工作空间 请访问原文链接:https://sysin.org/blog/omnissa-hor…