首页 > 其他分享 >最长不降子序列 n log n 方案输出与 Dilworth 定理 - 动态规划模板

最长不降子序列 n log n 方案输出与 Dilworth 定理 - 动态规划模板

时间:2024-07-22 12:40:00浏览次数:9  
标签:tmp log int tp 100005 si sn Dilworth 模板

朴素算法

不必多说,\(O(n^2)\) 的暴力 dp 转移。

优化算法

时间为 \(O(n \log n)\) ,本质是贪心,不是 dp 。

思路是维护一个单调栈(手写版),使这个栈单调不降。

  • 当该元素 \(\ge\) 栈顶元素时,把这个元素压入栈中。
  • 否则,在单调栈中找到第一个大于该元素的项,把这一项改为这个元素。(因为要使每个元素尽可能小,才有更大的拓展空间)。这个过程可以用二分实现,推荐使用码量更小的 upper_bound 或 lower_bound 。

最后,单调栈里元素个数即为最长不降子序列的长度。

#include <bits/stdc++.h>
using namespace std;
int n,a[100005],sn[100005],tp=0;
int main()
{
	sn[0]=-0x3f3f3f3f;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		if(a[i]>=sn[tp])
		{
			sn[++tp]=a[i];
		}
		else
		{
			int tmp=upper_bound(sn+1,sn+tp+1,a[i])-sn;
			sn[tmp]=a[i];
		}
	}
	cout<<tp<<endl;
	return 0;
}

输出方案

这样的贪心看似会破坏最长不降子序列的结构,不能输出;实际上我们可以通过记录一条链的方式输出正确的方案。

我们可以同时记录下单调栈里每个元素在原序列中的下标。

  • 对于直接压入栈顶的元素,我们把它在链表中的前驱设为它压入之前的栈顶元素。
  • 对于二分修改的元素,我们把它在链表中的前驱设为目前单调栈中它所处的位置的前一个元素。(不能设为被修改元素的前驱,因为这个前驱可能不是修改之后的。)

最后从栈顶的元素开始,遍历其前驱,记录在 vector 中,最后倒序输出即可。

#include <bits/stdc++.h>
using namespace std;
int n,a[100005],pre[100005],si[100005],sn[100005],tp=0;
vector<int>v;
int main()
{
	sn[0]=-0x3f3f3f3f;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		if(a[i]>=sn[tp])
		{
			pre[i]=si[tp];
			si[++tp]=i;
			sn[tp]=a[i];
		}
		else
		{
			int tmp=upper_bound(sn+1,sn+tp+1,a[i])-sn;
			pre[i]=si[tmp-1];
			si[tmp]=i;
			sn[tmp]=a[i];
		}
	}
	cout<<tp<<endl;
	int now=si[tp];
	while(now)
	{
		v.push_back(a[now]);
		now=pre[now];
	}
	reverse(v.begin(),v.end());
	for(auto x:v)cout<<x<<' ';
	return 0;
}

Dilworth 定理

挺难理解的,目前只能死记,或者通过贪心理解。

例题:导弹拦截第二问

Dilworth 定理:对于任何有限偏序集,其最大反链中元素的数目等于最小链覆盖中链的数目。

在导弹拦截中,“最小链覆盖” 就相当于是求最少要用多少导弹系统,而 “最大反链” 就相当于是本问的做法:**求最长上升子序列。(因为最小链指最长不升子序列,那么反链即为最长上升子序列)

导弹拦截-不记录方案版

#include <bits/stdc++.h>
using namespace std;
int n=1,h[100005],sn[100005],tp=0,un[100005];
bool cmp(const int &val,const int &e)
{
	return e<val;
}
int main()
{
	while(cin>>h[n])n++;
	n--;
	sn[0]=0x3f3f3f3f;
	for(int i=1;i<=n;i++)
	{
		if(sn[tp]>=h[i])
		{
			sn[++tp]=h[i];
		}
		else
		{
			int tmp=upper_bound(sn+1,sn+tp+1,h[i],cmp)-sn;
			sn[tmp]=h[i];
		}
	}
	cout<<tp<<endl;
	tp=0;
	for(int i=1;i<=n;i++)
	{
		if(un[tp]<h[i])
		{
			un[++tp]=h[i];
		}
		else
		{
			int tmp=lower_bound(un+1,un+tp+1,h[i])-un;
			un[tmp]=h[i];
		}
	}
	cout<<tp<<endl;
	return 0;
}

导弹拦截-记录方案版

#include <bits/stdc++.h>
using namespace std;
int n=1,h[100005],si[100005],sn[100005],tp=0,ui[100005],un[100005],pre[100005];
vector<int>v;
bool cmp(const int &val,const int &e)
{
	return e<val;
}
int main()
{
	while(cin>>h[n])n++;
	n--;
	sn[0]=0x3f3f3f3f;
	for(int i=1;i<=n;i++)
	{
		if(sn[tp]>=h[i])
		{
			pre[i]=si[tp];
			sn[++tp]=h[i];
			si[tp]=i;
		}
		else
		{
			int tmp=upper_bound(sn+1,sn+tp+1,h[i],cmp)-sn;
			pre[i]=si[tmp-1];
			sn[tmp]=h[i];
			si[tmp]=i;
		}
	}
	cout<<tp<<endl;
	int now=si[tp];
	while(now)
	{
		v.push_back(h[now]);
		now=pre[now];
	}
	reverse(v.begin(),v.end());
	for(auto x:v)cout<<x<<' ';
	cout<<endl;
	tp=0;
	for(int i=1;i<=n;i++)
	{
		if(un[tp]<h[i])
		{
			un[++tp]=h[i];
			ui[tp]=i;
		}
		else
		{
			int tmp=lower_bound(un+1,un+tp+1,h[i])-un;
			un[tmp]=h[i];
			ui[tmp]=i;
		}
	}
	cout<<tp<<endl;
	return 0;
}

标签:tmp,log,int,tp,100005,si,sn,Dilworth,模板
From: https://www.cnblogs.com/zhr0102/p/18315799

相关文章

  • GLOG(Google Logging Library) 基本使用
    安装Githubgooglelogginglibrary进入glog文件夹mkdirbuildcdbuildcmake..make-j8sudomakeinstall基本demo编译测试mkdirglog_democdglog_demogeditglog_demo.cppglog_demo.cpp:#include<glog/logging.h>intmain(intargc,char*argv[]){......
  • 【搜索】【模板】 IDDFS
    IDDFS使用场景使用dfs由于状态量太大会TLE,bfs会MLE。答案必须很小,一般在20以内。算法原理设置dfs的搜索深度限制\(dep\),dfs穷举\(dep\)层的答案。若在\(dep\)层找到满足条件的情形,则\(dep\)则为答案,否则\(dep\leftarrowdep+1\),继续搜索。优......
  • 【搜索】【模板】记忆化搜索
    记忆化搜索思想是实现DP的一种手段。优点:不关心递推顺序;对于两维及以上的DP,方便处理初始状态和无效状态。缺点:无法使用滚动数组。注意事项:要什么状态搜什么状态;所有的状态转移都要采取直接搜索的数据很傻。越界的状态不能赋值。实现步骤:先判断是否越界,如果越......
  • 【数学】【模板】扩展欧几里得算法
    扩展欧几里得算法思想首先回忆一下裴蜀定理:对于任意一对不全为\(0\)的整数\(a,b\),存在\(x,y\)使得\(ax+by=\gcd(a,b)\)。扩展欧几里得算法就是求出一组解满足\(ax+by=\gcd(a,b)\),这里用到了欧几里得算法,不会的,可以看看我的博客。我们看看怎么求:当\(b=......
  • 【数学】【模板】欧拉定理
    欧拉定理思想若\(a\)与\(n\)互质,则\(a^{\varphi(n)}\equiv1\pmodn\);容易得出当\(n\)为质数时,\(a^{n-1}\equiv1\pmodn\)。证明假设与\(1\simn\)中与\(n\)互质的数字为\(x_1,x_2,x_3,\cdotsx_{\varphi(n)}\),且两两不同。现将它们全部乘以\(a\)得......
  • 【数学】【模板】欧拉函数
    欧拉函数思想\(\varphi(n)\)表示的是\(1\simn\)中与\(n\)互质的个数。怎么求\(\varphi(n)\)呢?先将\(n\)质因数分解:\(n=p_1^{a_1}p_2^{a_2}\cdotsp_k^{a_k}\),那么\(\varphi(n)=n\left(1-\dfrac{1}{p_1}\right)\left(1-\dfrac{1}{p_2}\right)\cdots\left......
  • 【数学】【模板】欧几里得算法
    欧几里得算法思想\[\gcd(a,b)=\gcd(b,a\bmodb)\]证明\(\gcd(a,b)=\gcd(b,a\bmodb)\):首先,如果有\(d|a\),\(d|b\),那么\(d|ax+by\)。然后,\(a\bmodb=a-\left\lfloor\dfrac{a}{b}\right\rfloorb\)。假设\(p=\gcd(a,b)\),那么\(p|......
  • 【搜索】【模板】A* 算法
    学了IDA*,然后学学A*。A*A*可以简单理解为在bfs的时候分个先后,哪个最有可能先到达就先走哪一步。A*是在某个最短路问题中(比如求最小的步骤等),如果所有边权都是非负的,那么就可以使用启发函数来优化bfs过程。例题P1379八数码难题思路我们在bfs的过程中加入函数\(h......
  • 【数据结构】【模板】莫队
    莫队使用场景离线算法;区间询问不断修改。能用\(O(1)\)的时间复杂度从\([l,r]\)到\([l+1,r]\)或者\([l,r-1]\)。原理原问题可以转化为为建立一个坐标轴,对于一个询问\((l,r)\),相当于点\((l,r)\),从一个询问\((a,b)\)到\((c,d)\),可以理解为点\((a,b)......
  • 【图论】【模板】差分约束系统
    差分约束系统差分约束系统是将不等式组的问题转化为图论问题。前置知识判断负环例题P5960【模板】差分约束算法思路我们将\(x_u-x_v\ley_u\)换为\(x_u\lex_v+y_u\)。然后我们建立一条连接\(v,u\)(注意是\(v,u\)不是\(u,v\))权值为\(y_u\)的边。我们发......