首页 > 其他分享 >dp 套 dp 学习笔记

dp 套 dp 学习笔记

时间:2022-09-07 14:38:12浏览次数:81  
标签:LCS int text 笔记 内层 学习 mx dp

dp 的本质:通过不同的转移更新状态的答案,就像 DAG 上的拓扑一样。

dp 套 dp 的本质:将内层 dp 的答案作为外层 dp 的状态进行转移。

比如某个 dp 的状态为 \(f_{i,j}\),第二维不再仅仅是某个状态,而是代表内层 dp 的答案,如 \(f_{i.\text{vector<int>}j}\)。

当外层 dp 进行转移时,内层 dp 同时进行转移。

通常内层 dp 的答案会通过状压来在外层 dp 上表示。

P4590 [TJOI2018]游园会

题意:有一个长度为 \(n\) 的字符串 \(S\),其中不包含子串 NOI,和一个字符串 \(T\)。对于每一个 \(i\),求出 \(\text{LCS}(S,T)=i\) 的 \(S\) 有多少种。

先不考虑子串限制的条件。

如果设 \(f_{i,j}\) 表示 \(S\) 的前 \(i\) 个字符和 \(T\) 匹配的 \(\text{LCS}\) 长度为 \(j\) 时的字符串数量,会发现无法转移,因为不知道当前状态匹配的 \(\text{LCS}\) 到了哪里,也就是说,我们需要把 \(\text{LCS}\) 的匹配结果作为状态表示出来。这时候就需要 dp 套 dp 了。

我们先看内层 dp,求 \(\text{LCS}\)。

回想起 \(\text{LCS}\) 的 \(O(n^2)\) 做法,我们设 \(f_{i,j}\) 表示前 \(S\) 的前 \(i\) 个字符和 \(T\) 的前 \(j\) 个字符匹配的 \(\text{LCS}\)。

\[f_{i,j}= \left\{ \begin{aligned} f_{i-1,j-1}+1&&S_i=T_j\\ \max\{f_{i-1,j},f_{i,j-1}\}&&S_i\not= T_j\\ \end{aligned} \right. \]

发现每添加一个字符,相当于 \(f_i\) 转移到 \(f_{i+1}\),也就是说外层 dp 的第二维可以记为 \(f_i\) 这一层,每次转移时计算出 \(f_{i+1}\) 这一层就行。但是,这样时间和空间都爆炸了。

考虑优化,我们研究内层 dp,发现在 \(f_i\) 这一层中,\(f_{i,j+1}-f_{i,j}\in[0,1]\) ,差分出来就是一个 01 串,那么就可以对它进行状压了。

现在回到外层 dp 上,我们可以设 \(f_{i,j}\) 表示现在在 \(S\) 的第 \(i\) 位,匹配的 \(\text{LCS}\) 状压后成了 \(j\)。发现可以用滚动数组优化掉第一维。

现在重新来考虑子串限制的条件,我们只需要再开一维来记录匹配到了 NOI 的第 \(0/1/2\) 位就行。

时间复杂度为 \(O(k\times 2^k + n\times 2^k)\),空间复杂度为 \(O(n+2^k)\),常数都不小。

代码:

#include<bits/stdc++.h>
#define pc(x) putchar(x)
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){f=ch=='-'?-1:f;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
void write(int x)
{
	if(x<0){x=-x;putchar('-');}
	if(x>9)write(x/10);
	putchar(x%10+48);
}
const int mod=1e9+7;
char s[20];int mp[256];
int n,m,S,a[20],f[2][32770][3],ans[1005];
int g[2][1005],sta[32770][3],nxt[3][3]={{1,0,0},{1,2,0},{1,0,3}};
//f[i][j][k]表示前i个字符匹配的LCS的差分状压成j,NOI出现到了第0\1\2位
void init()//预处理出内层DP的转移
{
	for(int i=0;i<S;++i)
	{
		for(int j=1;j<=m;++j)g[0][j]=g[0][j-1]+((i>>(j-1))&1);
		for(int j=0;j<=2;++j)
		{
			for(int k=1;k<=m;++k)
			{
				g[1][k]=max(g[1][k-1],g[0][k]);
				if(a[k]==j)g[1][k]=max(g[1][k],g[0][k-1]+1);
			}int now=0;
			for(int k=1;k<=m;++k)now|=(1<<(k-1))*(g[1][k]>g[1][k-1]);
			sta[i][j]=now;
		}
	}
}
int main()
{
	n=read(),m=read();scanf("%s",s+1);S=(1<<m);
	mp['N']=0;mp['O']=1;mp['I']=2;
	for(int i=1;i<=m;++i)a[i]=mp[s[i]];//转序列
	init();f[0][0][0]=1;
	for(int i=1;i<=n;++i,memset(f[i&1],0,sizeof f[i&1]))
		for(int now=0;now<S;++now)
			for(int j=0;j<=2;++j)
				for(int k=0;k<=2;++k)
					if(j!=2||k!=2)(f[i&1][sta[now][k]][nxt[j][k]]+=f[i&1^1][now][j])%=mod;
	for(int now=0;now<S;++now)
	{
		int tmp=now,cnt=0;while(tmp)cnt+=tmp&1,tmp>>=1;
		for(int j=0;j<=2;++j)
			(ans[cnt]+=f[n&1][now][j])%=mod;
	}
	for(int i=0;i<=m;++i)write(ans[i]),pc('\n');
	return 0;
}

P4484 [BJWC2018]最长上升子序列

题意:现在有一个长度为 \(n\) 的随机排列,求它的最长上升子序列长度的期望。

正解应该是杨表,但是 dp 套 dp 可以打表 AC。

从前往后 dp,发现不能维护状态进行转移,所以从小往大来 dp。

设 \(f_i\) 表示排列中第 \(i\) 个数结尾的 \(\text{LIS}\) 长度,\(mx_i\) 为 \(f_i\) 的前缀最大值,可以发现 \(mx_i\le mx_{i+1}\le mx_i+1\)。

所以我们可以差分加状压来维护这个 \(mx\) 数组。

考虑现在在 \(i\) 和 \(i+1\) 间添加新数,那么这个数结尾的 \(\text{LIS}\) 长度一定为 \(mx_i+1\),由于上面的性质,所以差分数组中相当于在 \(i\) 位后面添加一个 \(1\),然后后面的遇到的第一个 \(1\) 变成 \(0\)(没有就不变)。

现在设计外层 dp,设 \(f_{i,j}\) 代表添加到 \(i\) 时,\(mx\) 的差分数组状压为 \(j\) 的排列数。

那么答案就是:

\[\sum_{i=0}^{2^n-1}f_{n,i}\times \text{__builtin_popcount}(i) \]

时间复杂度为 \(O(n^2\times 2^n)\),空间复杂度为 \(O(2^n)\)。显然都爆炸。

怎么办呢?本地打表,反正只有 \(28\) 个答案。

代码不贴了。

说是学习笔记,但就2道题(NOI2022D1T2还没补

标签:LCS,int,text,笔记,内层,学习,mx,dp
From: https://www.cnblogs.com/ctldragon/p/16665256.html

相关文章

  • 直播平台源代码,uniapp中样式的学习及如何使用scss和字体图标
    直播平台源代码,uniapp中样式的学习及如何使用scss和字体图标uni-app中的样式rpx即响应式px,一种根据屏幕自适应动态单位。以750宽的屏幕为基准,750rpx恰好为屏幕宽度。屏......
  • 【论文笔记】LayoutLMv2:将视觉信息加入到预训练阶段的跨模态文档预训练模型
    概述LayoutLMv2是对LayoutLM的改进,主要有以下几点区别:将视觉信息加入到了预训练阶段,而不是LayouLM中的微调阶段删除了MDC,添加了text-imagealignment和text-imgaematc......
  • java学习笔记20
    增强for循环JAVA5引入一种主要用于数组或集合的增强型for循环格式如下for(声明语句:表达式){//代码句子}publicclassForDemo05{  publicstaticvoidmain(Strin......
  • 外卖系统学习笔记
    开发日记:用户登录界面:前端发送ajax请求,后端对请求处理,去数据库查询信息,匹配成功后将用户id存入session,并返回登录成功。并添加拦截器,获取前端请求的URL,判断请求路径是否正......
  • Flink学习
    一、Flink部署1.集群角色:hadoop102:JobManager;hadoop103:TaskManager;hadoop104:TaskManager2.集群启动$bin/start-cluster.sh3.查看flink状态:jps4.停止集群$b......
  • 随记笔记
    事事往往像硬币两面,一边是头一边是字,你不能只看他一边哪,凡是哪有谁是全对,谁是全错的,总之但求情之所在,心之所安就算了。1、聪明,只适用于当时,而智慧可以永久,传承。就像酒......
  • 第一天学习 html 基础
    1、web标准的构成: 《结构Structure》(对应html文件)、《表现Presentation》(对应css文件)和《行为Behavior》(对应js)三个方面2、骨架标签<html>//根标签<head></head>......
  • Vben Admin 源码学习:状态管理-角色权限
    前言本文将对Vue-Vben-Admin角色权限的状态管理进行源码解读,耐心读完,相信您一定会有所收获!更多系列文章详见专栏......
  • Salesforce学习收藏贴!一文搞懂Salesforce角色、简档和权限集
     简档、角色和权限集共同决定Salesforce用户可以在Salesforce中查看和执行的操作。【安全和访问】算是Salesforce管理员认证考试中最棘手的模块之一,作为该模块的重要考......
  • 关于WIN7下无法修改网络类型的一种解决办法(笔记)
    今天发现WIN7旗舰版的一个网卡无法修改网络类型,一直显示是无法识别的网络,另一个网卡则可以修改按网络上的方法尝试:组策略无效偶然翻到一篇文章:https://www.gqgtpc.com/t......