[Luogu2843] [USACO09MAR,Gold] Cleaning Up [dp]

l i n k \frak{link} link

本篇题解可能含有大量窝理解缓慢写出来的废话
下面主要是讲推出式子的思路
如果大佬们觉得一些地方很显然的话还是选择性跳着读吧(
解题方法看代码就看得出来的

这种类型的不好直接转移,
如果只知道区间的大概信息也不知道有哪些数被选(状压啥的在分析之前就被数据范围 x \mathsf{x} x掉了
所以得另外开一个数组预处理一些信息:比如 [ i , j ] [i,j] [i,j]里面的数值种类数 d i f f [ i ] [ j ] \mathfrak{diff[i][j]} diff[i][j]
方程 f i = m i n { f j + d i f f j ∼ i 2 } \mathfrak{f_{i}=min\{f_j+diff_{j\thicksim i}^2\}} fi=min{fj+diffji2}
然而这题不给 Θ ( n 2 ) \mathfrak{\Theta(n^2)} Θ(n2)。那么 d i f f [ i ] [ j ] \mathfrak{diff[i][j]} diff[i][j]就别想了
d p \mathfrak{dp} dp暂且到此为止。

——换一种思路。考虑极端情况来入手。
比如讲,如果一个也不分, a n s = n 2 \mathfrak{ans=n^2} ans=n2
中间的略过,先考虑更直接简单的情况:如果每一个都分出来, a n s \mathfrak{ans} ans
= n \mathfrak{=n} =n
那么,最优解一定有:它的 a n s ≤ n \mathfrak{ans}\le n ansn
放在长度 l e n \mathfrak{len} len的小区间上有 a n s ′ ≤ l e n \mathfrak{ans'\le len} anslen,于是一段区间里面不同种类数的种数不能超过 l e n \mathfrak{\sqrt{len}} len
所以怎么利用?
这里差点歪出一颗主席树来

好像复杂度是 Θ ( n n ) \mathfrak{\Theta(n\sqrt{n})} Θ(nn )了吧?
其中 n \mathfrak{\sqrt{n}} n 的产生方式有可能会是要枚举 n \mathfrak{\sqrt{n}} n
…?这么想呢。能够枚举到 n \mathfrak{\sqrt{n}} n 的也就是种数了?
回到 d p \mathfrak{dp} dp考虑。现在空间上地、有两维。
第一维多半是位置。(可以滚掉)
第二维也一般和上一个集合有关。
放到这题里面,如果第二维只表示到现在有几个集合那好像没有什么用。
需要表示的,有可能是 d i f f \mathfrak{diff} diff;不过为了转移这个一定不能放进去,
在二维里面的应该是能确定更多关于之前集合信息的:比如上一个集合的末端。
至于 d i f f \mathfrak{diff} diff可以间接来决定,例如说,决定“上一个集合”——

考虑时间上,两层循环?
第一层是位置、递增。记为 i \mathfrak{i} i
既然说要用 d i f f \mathfrak{diff} diff来间接决定空间二维,那时间的二维就是它了。 j \mathfrak{j} j
可以记 p o s j \mathfrak{pos_j} posj 表示当前位置 i \mathfrak{i} i,往前面的一个从其右端点右侧到 i \mathfrak{i} i d i f f ≤ j \mathfrak{diff\le j} diffj的区间。
然后转移就是选进(这个区间右端点右侧 到 i \mathfrak{i} i)的区间。
当然选进来的区间越长越好,反正它能被选就是它的 d i f f \mathfrak{diff} diff符合要求 ≤ j \le\mathfrak{j} j

啊对了,到这里不用求 d i f f \mathfrak{diff} diff,而是要求最长 d i f f = j \mathfrak{diff=j} diff=j
实现显然可以对于一个数处理跟他相同的数(位置上前驱/后继),
对于 p o s x \mathfrak{pos_x} posx同时处理 c n t x \mathfrak{cnt_x} cntx表示 a p o s x \mathfrak{a_{pos_x}} aposx出现几次了。

复杂度: Θ ( n n ) \mathfrak{\Theta(n\sqrt{n})} Θ(nn )

f i = m i n { f p o s j + j 2 } \mathfrak{f_i=min\{f_{pos_j}+j^2\}} fi=min{fposj+j2}
维护一下 n \mathfrak{\sqrt{n}} n p o s j \mathfrak{pos_j} posj

我怎么分析了这么长(鶸爆了

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<queue>
#include<cstring>
#include<cctype>
using namespace std;
int N,M,L,A;
int P[40005]={};
int F[40005]={};
int head[40005]={};
int nxt[40005]={};
int pre[40005]={};
int cnt[40005]={};
int diff=0;
int main()
{
	scanf("%d%d",&N,&M); L=sqrt(N);
	for(int i=1;i<=N;++i)
	{
		nxt[i]=N+1;
		scanf("%d",&A);
		pre[i]=head[A];
		nxt[head[A]]=i;
		head[A]=i;
		F[i]=0x3f3f3f3f;
	}
	nxt[N+1]=N+1;
	for(int t,i=1;i<=N;++i)
	{
		if(!pre[i])++diff;
		t=min(diff,L); //*
		for(int j=1;j<=t;++j)
		{
			if(!cnt[j])cnt[j]=diff; else if(pre[i]<=P[j])++cnt[j];
			while(cnt[j]>j)if(nxt[++P[j]]>i)--cnt[j];
			F[i]=min(F[i],F[P[j]]+j*j);
		}	
	}
	printf("%d",F[N]);
    return 0;
}

Summary

d p \mathfrak{dp} dp的过程实际上(大约)就是在一个状态的 S − T &ThickSpace; D A G \mathcal{S-T\;DAG} STDAG上面逐层(可能高维)跑答案
因此 d p \mathfrak{dp} dp要满足无后效性和最优化子结构性质。

已经最终决定的子问题,它和推出它的子问题里面只有它会对后面的问题有贡献。
子问题最优解可以得到原问题最优解。(实际上就是 f \mathfrak{f} f是有效的,能够求解问题)

同时,

d p \mathfrak{dp} dp是由搜索演变来的,而相比搜索, d p \mathfrak{dp} dp牺牲了空间复杂度存状态来换取时间复杂度的降低。
d p \mathfrak{dp} dp存储状态,就是为了避免像朴素递归穷举那样多次重复求解同一个问题
这一点上 d p \mathfrak{dp} dp或许也可以算作一种剪枝?
由此, d p \mathfrak{dp} dp很大的作用就是避免多次解决重复问题。
于是在题目的一层问题上,一般来说能分治就不 d p \mathfrak{dp} dp,能 d p \mathfrak{dp} dp就不分治。
因为能分治(不考虑那些奇奇怪怪的分治)就是子问题不带重复
子问题带重复就分治不了。

可以由以上三个性质来考虑问题是否能用 d p \mathfrak{dp} dp解决。
(当然 d p \mathfrak{dp} dp做多了、这种考虑可能会很自然)
有这么一种说法,“贪心是第一数学归纳法, d p \mathfrak{dp} dp是第二数学归纳法”
贪心, f i → f i + 1 \mathfrak{f_i\to f_{i+1}} fifi+1
d p \mathfrak{dp} dp f 1 ∼ i → f i + 1 \mathfrak{f_{1\thicksim i}\to f_{i+1}} f1ifi+1

寻找朴素 d p \mathfrak{dp} dp方程的方法就是尽量满足三个性质。
优化的方法?
空间优化基本上就那么样了。
时间上,

优化/设计方法
  1. 利用数据结构优化(比如转移方程里面有区间x之类的操作)或者数学变换
  2. 利用题目本身的性质(单调性、区间可加之类)
  3. 对于特定形式的方程,比如四边形不等式,斜率优化,单调队列
  4. 将状态延迟处理,比如把 f i \mathfrak{f_i} fi沿用到 f i + j \mathfrak{f_{i+j}} fi+j,有时可以优化掉一维;类似的前缀和
  5. 某一类问题(插头、轮廓线、计数、状压、期望、概率等)有比较通用的思路
  6. 分治偶尔可以跟 d p \mathfrak{dp} dp一起用, l o g \mathfrak{log} log掉一个 n \mathfrak{n} n
  7. 一类带修改的动态 d p \mathfrak{dp} dp问题(就不在这里详谈吧)
  8. 利用自动机等构造 d f a \mathfrak{dfa} dfa转移图并且可能可以用于 d p \mathfrak{dp} dp上;矩阵加速
  9. 推结论。甚至可以推倒dp
  10. 逆向思维
  11. 在某些部分可以尝试结合其他算法,比如 d p \mathfrak{dp} dp+图论啥的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值