首页 > 其他分享 >动态规划

动态规划

时间:2024-10-01 21:46:20浏览次数:6  
标签:int register pos LIS 序列 动态 规划 dp

动态规划

这一篇完全写不完,只能把今天回顾的内容记录一遍,所以之后肯定会补充。

概念性知识(使用条件)

最优子结构

即:一个情形面前只有有限个抉择,那么要想让当前得到的结果最优,那么一定会去贪心地做出选择。

无后效性

把问题划分成阶段,那么按照逻辑顺序,当前阶段的决策不会受到之后所做的决策的影响。

可继承性

对于第 \(i-1\) 个阶段,它是第 \(i\) 个阶段的子问题,言下之意就是,前者的最优答案可以直接被后者继承,这一点在大部分实际问题中的意义表现为:在第 \(i\) 个阶段不做任何事 / 不选任何东西,直接保留 \(i-1\) 时的答案为最优解。

要素

阶段状态转移方程初始条件答案

基础问题 LIS 与 LCS

LIS 最长上升子序列

定义 \(dp[i]\) 为:以第 \(i\) 个数结尾的最长 LIS 长度

很容易写出一个 \(O(n^2)\) 的暴力转移方程 \(dp[i]=\max_j^{a[i]>a[j]}(dp[j]+1)\),这样做在数据范围较大的情况下是会超时的。

所以我们考虑换个方式来定义 \(dp\) ,\(dp[len]\) 为:长度为 \(len\) 的 LIS 结尾数字的最小值,很显然的是在这种定义下我们只需要使用贪心的思想来更新即可,并且这样的话就出现了一个美妙的性质: \(dp\) 数组一定是单调增加的,因此可以在查找的时候二分。

枚举每一个 \(a[i]\) ,二分查找 \(dp\) 数组中最后一个小于 \(a[i]\) 的数 ,同时更新 \(dp[len]\)即可。

Code

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int dp[N],a[N],n;
int main()
{
	int n;
	cin>>n;
	for(register int i=1;i<=n;++i)cin>>a[i];
	memset(dp,0x3f,sizeof dp);
	int ans=-1;
	for(register int i=1;i<=n;++i)
	{
		int pos=lower_bound(dp+1,dp+n+1,a[i])-dp-1;
		if(pos+1>ans)ans=pos+1;
		dp[pos+1]=min(dp[pos+1],a[i]);
	}
	cout<<ans;
	return 0;
 } 

LCS最长公共子序列问题

定义 \(dp[i][j]\) 为:序列A考虑了前 \(i\) 个,序列B考虑了前 \(j\) 个的最长LCS长度。

当 \(A[i]=B[j]\) 的时候,\(dp[i][j]=dp[i-1][j-1]+1\) 。

否则,\(dp[i][j]=max(dp[i-1][j],dp[i][j-1])\) 。

这样做是 $O(nm) $ 的。

对于特殊情况,如 Luogu P1439,两个序列的元素实际上都相同(只是不对应相同),还有 \(O(n\log n)\) 的写法。

Code-Brute Force Ver.

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n;
	cin>>n;
	int a[n+10],b[n+10],dp[n+10][n+10];
	for(register int i=1;i<=n;++i)cin>>a[i];
	for(register int i=1;i<=n;++i)cin>>b[i];
	for(register int i=0;i<=n;++i)
		for(register int j=0;j<=n;++j)dp[i][j]=0;
	for(register int i=1;i<=n;++i)
	{
		for(register int j=1;j<=n;++j)
		{
			dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
			if(a[i]==b[j])dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
		}
	}
	cout<<dp[n][n];
	return 0;
}

Code-Better Ver.

随便给定两个题意序列 \({1,5,2,3,6,4}\) 和 $2,4,5,6,3,1 $ 其中前一个序列在后一个序列中出现的位置分别是 \(6,3,1,5,4,2\)

我们发现,从第二个序列中选出一个子序列,其位置肯定是递增的,这两者是充要关系。

那么如果我们从第三个序列(也就是抽象过后的第一个序列)中选择一段递增的数字,那么其必定是序列一二的公共子序列;进一步地,我们选择第三个序列的LIS,那么其对应的就是序列一二的 LCS。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
int dp[N],a[N],b[N],idx[N];
int main()
{
	int n;
	cin>>n;
	for(register int i=1;i<=n;++i)
	{
		cin>>a[i];
		idx[a[i]]=i;
	}
	for(register int i=1;i<=n;++i)
	{
		cin>>b[i];
		a[idx[b[i]]]=i;
	}
	int ans=-1;
	memset(dp,0x3f,sizeof dp);
	for(register int i=1;i<=n;++i)
	{
		int pos=lower_bound(dp+1,dp+i,a[i])-dp-1;
		if(pos+1>ans)ans=pos+1;
		dp[pos+1]=min(dp[pos+1],a[i]);
	}
	cout<<ans;
	return 0;
}

记忆化搜索

比如用递归求解 Fibonacci 的时候,我们会经常访问同一个子问题 \(f(i)\) ,而每次都去重复地用同样的过程去求解它,这显然是无用功,所以我们用一个数组来记录每个状态对应的值,如果它没有被计算过,我们就去调用函数,并且存储最后计算出来的值;反之,直接访问数组内的值,这样就可以极大地优化效率。

下面是记忆化求 FIbonacci 的示例:

int f[N]
int get(int x)
{
	if(f[x])return f[x];
	if(x==1||x==2)return 1;
	return f[x]=get(x-1)+get(x-2);
}

标签:int,register,pos,LIS,序列,动态,规划,dp
From: https://www.cnblogs.com/Hanggoash/p/18443858

相关文章

  • 网络流与线性规划24题详解(上)
    前言题单刷24题刷魔怔了,写个详解。难度不断递增,T1-T9为蓝题,T10-T23为紫题。(什么?你问我为什么没有T24?)好了,让我们开始吧!T1孤岛营救问题思路:这题数据小,所以用BFS\(key[x][y][k]\)记录\((x,y)\)的第k把钥匙\(wall[x1][y1][x2][y2]\)记录墙和门\(vis[x1][y1][k]\)记录是否走......
  • 反射 动态代理
    出自https://www.bilibili.com/video/BV1ke4y1w7yn1.反射1.1反射的概述:​ 专业的解释(了解一下):​是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;​对于任意一个对象,都能够调用它的任意属性和方法;​这种动态获取信息以及动态调用对......
  • 路径规划
    独立做出了百度之星决赛的金牌题,开心~动态转移的时候直接新开一个数组存储历史值,更清晰简洁,不给自己找麻烦在memcpy语句中,被操纵的数组在前点击查看代码#include<bits/stdc++.h>usingnamespacestd;vector<int>a[100005],c[100005];intcnt[100005];longlongf[1000......
  • 每日OJ题_牛客_DP2跳台阶_动态规划_C++_Java
    目录牛客_DP2跳台阶_动态规划题目解析C++代码Java代码牛客_DP2跳台阶_动态规划跳台阶_牛客题霸_牛客网题目解析        当前值只和数组的前两个值有关,在往前面的就无关了,所以没必要申请一个数组,直接使用两个变量即可,这样空间复杂度就满足要求了。C++代码......
  • [rCore学习笔记 028] Rust 中的动态内存分配
    引言想起我们之前在学习C的时候,总是提到malloc,总是提起,使用malloc现场申请的内存是属于堆,而直接定义的变量内存属于栈.还记得当初学习STM32的时候CubeIDE要设置stack和heap的大小.但是我们要记得,这么好用的功能,实际上是操作系统在负重前行.那么为了实现动态内存分配功......
  • leetcode刷题day34|动态规划Part03 背包问题(01背包问题 二维、01背包问题 一维、416.
    0-1背包问题二维动规五部曲1、确定dp数组以及下标的含义dp[i][j]表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。(取物品时可以是取0-i的任意一个,或者是他们的组合)2、确定递推公式不放物品i:背包容量为j,里面不放物品i的最大价值是dp[i-1][j]......
  • leetcode刷题day33|动态规划Part02(62.不同路径、63. 不同路径 II、 343.整数拆分、96.
    62.不同路径机器人从(0,0)位置出发,到(m-1,n-1)终点。动规五部曲1、确定dp数组(dptable)以及下标的含义dp[i][j]:表示从(0,0)出发,到(i,j)有dp[i][j]条不同的路径。2、确定递推公式想要求dp[i][j],只能有两个方向来推导出来,即dp[i-1][j]和dp[i][j-1]。dp[i]......
  • jdk动态代理
     1.定义接口2.实现接口的具体类3.创建InvocationHandler实现importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;publicclassMyInvocationHandlerimplementsInvocationHandler{privateObjecttarget;publicMyInvocationH......
  • JavaScript 网页设计案例 简单的电商案例 页面切换 数据搜索 动态网页
    JavaScript网页设计案例简单的电商案例页面切换数据搜索动态网页1.案例描述以下是一个简单的产品展示网页,用户可以通过点击不同的产品类别按钮来查看相应的产品,且在鼠标悬停时显示产品详情。页面还将包含一个搜索框,用户可以输入关键词来筛选产品。2.文件结构-......
  • 数据通信——动态路由协议RIP
    目录一.动态路由协议分类二.距离矢量路由协议(理解)三. 链路状态路由协议(理解)四.RIP的工作原理五.路由表的形成过程 六.RIP的度量值(条数)cost七.RIP的版本(v1和v2)八.RIP解决路由环路(2)水平分割:从一接口上学到的路由信息,不会再从这个接口上发出去(3)毒性逆转(与水平分割......