【ACM程序设计】动态规划 第二篇 LCS&LIS问题
动态规划
P1439 【模板】最长公共子序列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
给出 1,2,…,n 的两个排列 P1 和 P2 ,求它们的最长公共子序列。
输入格式
第一行是一个数 n。
接下来两行,每行为 n 个数,为自然数 1,2,…,n 的一个排列。
输出格式
一个数,即最长公共子序列的长度。
输入输出样例
输入 #1
5
3 2 1 4 5
1 2 3 4 5
输出 #1
3
说明/提示
- 对于 50% 的数据, n≤1000;
- 对于 100% 的数据, n≤100000。
首先 我们区分两个概念:
- 子序列:序列的一部分项按原有次序排列而得的序列,也是说 这里 如 3 1 5 也算子序列
- 子串:串的连续一部分
- 排列:从1~n不重复出现
这题如何用dp来解决?首先,我们把序列分为X和Y两个序列。
我们尝试寻找一个最优子结构和定义一个状态来表示公共子序列的大小。
于是,我们可以想到:从两个串的第一位开始逐个对比到至最后。我们定义状态d( i , j ),表示两个串 X 对比到第 i 位, Y 对比到第 j 位这个状态下的最长公共子序列,那么d(n,n)即为原题目的解。
我们可以写出状态转移方程:
由此,我们可以写出代码:
#include<stdio.h>
const int N = 1e3 + 7; //1007
int x[N];
int y[N];
int d[N][N];
int max(int x, int y) { return x > y ? x : y; }
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &x[i]);
for (int i = 1; i <= n; i++)
scanf("%d", &y[i]);
for (int i = 1; i <= n; i++)
d[i][0] = d[0][i] = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
if (x[i] == y[j])
d[i][j] = d[i - 1][j - 1] + 1;
else d[i][j] = max(d[i - 1][j], d[i][j - 1]);
}
printf("%d", d[n][n]);
return 0;
}
我们尝试对空间进行优化。
会发现,每次的d(I,j)都是由它左边d(I,j-1)或者上边d(i-1,j)或者左上的值转变而来。
那么我们是不是就可以用一个二维数组来滚动代替储存呢?
这样,我们就把第一维的空间压缩为2
由于我们循环执行的次数是n^2,所以时间复杂度是O(1e10),必定超时啊。
LCS&LIS问题
如果你能想出朴素的dp算法那你在第一层,能够用滚动数组优化空间那你在第二层,然而出题人在第五层,算法竞赛就是这样。
我们发现两个数组都是全排列数组,也就是说a中的数字在b中只会出现一次,a中没出现的数字b中也不会出现,b只是a的另一种排列顺序。
那么我们以a的顺序为基准按出现的时机进行记录后再对b中的数字按照记录标记那么b中只有出现时机单调递增的子序列是符合题目的,这就让题目从LCS问题变为了LIS问题。
LCS问题:最长公共子序列问题
LIS问题: 最长上升子序列问题
ind[num] = i; data[i]=ind[num];
举个例子 求 1 7 6 2 3 4最长上升子序列
定义状态:d(i)表示以第i个数字结尾的最长上升子序列
状态转移:
初始状态:
对于每一个数来说,最长上升序列就是本身,即d [ i ] 的初始值为1
#include<stdio.h>
int a[100];
int dp[100];
int max(int x, int y) { return x > y ? x : y; }
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
dp[i]=1;
for(int j=1;j<i;j++)
{
if(a[j]<a[i])
dp=max(dp[i],dp[j]+1);
}
printf("dp[%d]=%d",i,dp[i]);
}
return 0;
}
num 3 2 1 4 5 1 2 3 4 5
ind[3]=1,ind[2]=2,ind[1]=3,ind[4]=4,ind[5]=5
data[1]=ind[1]=3,data[2]=ind[2]=2,data[3]=ind[3]=1,data[4]=ind[4]=4,data[5]=ind[5]=5
dp[1]=1 i=2 j=1 data[1]>data[2],dp[2]=1 i=3 j=1 dp[3]=1 j=2 dp[3]=1
i=4 j=1 dp[4]=max(dp[4],dp[1]+1)=2 j=2 dp[4]=2
i=5 j=1 dp[5]=3
ans=3
但是 时间还是n^2
1 7 6 2 3 4
为了优化,我们可以另外开一个单调的数组,用于储存上升的数。
设置一个答案序列B,初始为空。第一次搜索到了1,将1加入答案序列,然后到了7,7>1故加入序列,随后到
了数字6,我们找到序列中第一个大于该数字的数,用该数字进行替换。
这是因为我们只需要求出长度,这样子替换不会对最终的答案造成影响可以视为答案序列被替换后的6即表示原序列6的位置,也表示7的位置。
最终答案序列是{1,2(6,7),3,4}
假如是5 2 3 1 4,最终答案序列是{1(2,5),3,4} 可以看出1的位置能够表示2或者5,最终序列的答案也是2,3,4。
由于该队列的严格单调,所以我们使用二分的方法去查找。
最后的答案即队列的长度。
1,7,6,2,3,4
i=1 {1}
i=2 {1,7}
i=3 {1,6(7)} 因为6比7小,覆盖了7可以使来了更大的数可以延长这个序列
i=4 {1,2(6,7)} 同理,其作用在下一行表现出来了
i=5 {1,2(6,7),3}
i=6 {1,2(6,7),3,4}
答案为4。
#include<stdio.h>
const int N = 1e5 + 7;
int data[N], dp[N], ind[N];
int goal[N]; //队列
int num, ans;
int max(int x, int y) { return x > y ? x : y; }
//二分查找
int search(int l, int r, int num)
{
while (l < r)
{
int mid = (l + r) >> 1;
if (num <= goal[mid]) r = mid;
else l = mid + 1;
}
return l;
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", &num);
ind[num] = i;
}
for (int i = 1; i <= n; i++)
{
scanf("%d", &num);
data[i] = ind[num];
}
int len = 1;
goal[1] = data[1];
int pos = 0;
for (int i = 2; i <= n; i++)
{
//data[i]>队尾元素故加入序列
if (goal[len] < data[i])
goal[++len] = data[i];
//查找到第一个大于data[i]的数,用该数字进行替换
else
goal[search(1, len, data[i])] = data[i];
}
printf("%d", len);
return 0;
}
这样我们的时间可以化为O(n*log n)
【ACM程序设计】动态规划 第二篇 LCS&LIS问题的更多相关文章
- 华南师大 2017 年 ACM 程序设计竞赛新生初赛题解
题解 被你们虐了千百遍的题目和 OJ 也很累的,也想要休息,所以你们别想了,行行好放过它们,我们来看题解吧... A. 诡异的计数法 Description cgy 太喜欢质数了以至于他计数也需要用质 ...
- 深入理解javascript对象系列第二篇——属性操作
× 目录 [1]查询 [2]设置 [3]删除[4]继承 前面的话 对于对象来说,属性操作是绕不开的话题.类似于“增删改查”的基本操作,属性操作分为属性查询.属性设置.属性删除,还包括属性继承.本文是对 ...
- 深入理解javascript函数系列第二篇——函数参数
× 目录 [1]arguments [2]内部属性 [3]函数重载[4]参数传递 前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传 ...
- 20145213《Java程序设计》第二周学习总结
20145213<Java程序设计>第二周学习总结 教材学习内容总结 本周娄老师给的任务是学习教材的第三章--基础语法.其实我觉得还蛮轻松的,因为在翻开厚重的书本,一股熟悉的气息扑面而来, ...
- 20145337 《Java程序设计》第二周学习总结
20145337 <Java程序设计>第二周学习总结 教材学习内容总结 Java可分基本类型与类类型: 基本类型分整数(short.int.long).字节(byte).浮点数(float ...
- 《Java程序设计》第二周学习总结
20145224陈颢文<Java程序设计>第二周学习总结 教材学习内容总结 一.类型.变量与运算符 1.类型 整数: 可细分为为short整数(占2字节),int整数(占4字节),long ...
- 西南科技大学第十一届ACM程序设计大赛发言稿
西南科技大学第十一届ACM程序设计大赛发言稿 各位老师.志愿者及参赛选手: 大家好,我是来自计科学院卓软1301的哈特13,很荣幸今天能站在这里代表参赛选手发言. 回想起来,我参加ACM比赛已经快两年 ...
- 记第五届山东省ACM程序设计比赛——遗憾并非遗憾
记第五届山东省ACM程序设计比赛 5月10日上午9点半左右,我们的队伍从学校出发,一个多小时后到达本次比赛的地点-哈尔滨工业大学. 报道,领材料,吃午饭,在哈工大的校园里逛了逛,去主楼的自习室歇息了一 ...
- 20155304田宜楠 2006-2007-2 《Java程序设计》第二周学习总结
20155304田宜楠 2006-2007-2 <Java程序设计>第二周学习总结 教材学习内容总结 一.类型与变量 1.类型 整数: 可细分为为short整数(占2字节),int整数(占 ...
随机推荐
- es8(字符串,对象)
es8(字符串,对象) 字符串补白: let str = "abc"; let a = str.padEnd(5); let b = str.padStart(5); let c ...
- java中封装encapsulate的概念
封装encapsulate的概念:就是把一部分属性和方法非公有化,从而控制谁可以访问他们. https://blog.csdn.net/qq_44639795/article/details/1018 ...
- java读取xml文件并转换成对象,并进行修改
1.首先要写工具类,处理读取和写入xml文件使用的工具.XMLUtil.javaimport java.io.FileInputStream; import java.io.FileWriter; i ...
- 什么是3D建模?
一.3D建模是什么 将所见所想用立体三维的方式通过计算机技术表现出来,这就是3D建模,比如你看到了一个可爱呆萌的卡通人物又或是华丽酷炫的变形金刚,总之就是你认为美好的立体实物 你想将它用计算机技术完美 ...
- 查看k8s的yaml里面反馈的错误,不仅仅是通过日志来查看错误
使用:kubectl describe pod......例如: kubectl describe pod api-55f5d8d49d-kzmcj ///////////////////////// ...
- Docker引擎核心组件
Docker Engine(Docker引擎)是Docker的核心部分,使用的是客户端-服务器(C/S)架构模式,其主要组成部分如下图所示. 从上图中可以看出,Docker Engine中包含了三个核 ...
- python入门基础-介绍、基础语法
一.anaconda下的spyder简介 Spyder 是一个强大的交互式 Python 语言开发环境,提供高级的代码编辑.交互测试.调试等特性,支持包括 Windows.Linux 和 OS X 系 ...
- .net 使用Docker开发
.NET多年以前已经开始支持Docker,但由于国内.net现状,生产过程中几乎用不到docker支持,趁着有点时间捣鼓下~. 先期工作 1.首先安装 Docker Desktop 2.安装Visua ...
- OpenHarmony 3.1 Beta版本关键特性解析——探秘隐式查询
(以下内容来自开发者分享,不代表 OpenHarmony 项目群工作委员会观点) 徐浩 隐式查询是 OpenAtom OpenHarmony(以下简称"OpenHarmony" ...
- 2021-01-25 cf #697 Div3 C题(超时,换思路减少复杂度)
题目链接:https://codeforces.com/contest/1475/problem/C 题意要求:需组成的2对,男的序号不能重,女的序号不能重 比如这例 输入: 行1--测试个数 行1` ...