动态规划

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问题的更多相关文章

  1. 华南师大 2017 年 ACM 程序设计竞赛新生初赛题解

    题解 被你们虐了千百遍的题目和 OJ 也很累的,也想要休息,所以你们别想了,行行好放过它们,我们来看题解吧... A. 诡异的计数法 Description cgy 太喜欢质数了以至于他计数也需要用质 ...

  2. 深入理解javascript对象系列第二篇——属性操作

    × 目录 [1]查询 [2]设置 [3]删除[4]继承 前面的话 对于对象来说,属性操作是绕不开的话题.类似于“增删改查”的基本操作,属性操作分为属性查询.属性设置.属性删除,还包括属性继承.本文是对 ...

  3. 深入理解javascript函数系列第二篇——函数参数

    × 目录 [1]arguments [2]内部属性 [3]函数重载[4]参数传递 前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传 ...

  4. 20145213《Java程序设计》第二周学习总结

    20145213<Java程序设计>第二周学习总结 教材学习内容总结 本周娄老师给的任务是学习教材的第三章--基础语法.其实我觉得还蛮轻松的,因为在翻开厚重的书本,一股熟悉的气息扑面而来, ...

  5. 20145337 《Java程序设计》第二周学习总结

    20145337 <Java程序设计>第二周学习总结 教材学习内容总结 Java可分基本类型与类类型: 基本类型分整数(short.int.long).字节(byte).浮点数(float ...

  6. 《Java程序设计》第二周学习总结

    20145224陈颢文<Java程序设计>第二周学习总结 教材学习内容总结 一.类型.变量与运算符 1.类型 整数: 可细分为为short整数(占2字节),int整数(占4字节),long ...

  7. 西南科技大学第十一届ACM程序设计大赛发言稿

    西南科技大学第十一届ACM程序设计大赛发言稿 各位老师.志愿者及参赛选手: 大家好,我是来自计科学院卓软1301的哈特13,很荣幸今天能站在这里代表参赛选手发言. 回想起来,我参加ACM比赛已经快两年 ...

  8. 记第五届山东省ACM程序设计比赛——遗憾并非遗憾

    记第五届山东省ACM程序设计比赛 5月10日上午9点半左右,我们的队伍从学校出发,一个多小时后到达本次比赛的地点-哈尔滨工业大学. 报道,领材料,吃午饭,在哈工大的校园里逛了逛,去主楼的自习室歇息了一 ...

  9. 20155304田宜楠 2006-2007-2 《Java程序设计》第二周学习总结

    20155304田宜楠 2006-2007-2 <Java程序设计>第二周学习总结 教材学习内容总结 一.类型与变量 1.类型 整数: 可细分为为short整数(占2字节),int整数(占 ...

随机推荐

  1. 前端基础之CSS(1)

    1.css3的新特性有哪些 (1)CSS3选择器(基本.属性.伪类具体见下) (2)CSS3边框与圆角 圆角border-radius 属性:border-top-left-radius 左上角 bo ...

  2. java中什么是局部内部类Local inner class?

    5.局部内部类Local inner class 马克-to-win:什么叫局部内部类?内部类声明位置:1.它的外部类的范围之内.2.在几个程序块的范围之内.例如,由方法定义的块中或甚至在for循环体 ...

  3. PL/SQL中的 not

    ELECT * FROM table_name WHERE column_name not like'%山%' 這時出現了column_name中為null值的情況也被剔掉了. 原因是:在SQL的表達 ...

  4. 在create-react-app使用less与antd按需加载

    使用antd按需加载 使用react-app-rewired对 create-react-app 的默认配置进行自定义 yarn add react-app-rewired --dev /* pack ...

  5. 去掉有定位的left值

    left: initial;   一开始就是初始(默认值)的意思,就可以解决定位的left啦

  6. 联机算法和脱机算法[Alg_001]

    ​ 一.联机算法 1.定义 也叫在线算法,在算法执行过程中的任意时刻,只对要操作的数据进行一次扫描,扫描完成后便此后不再对已经操作过的数据进行保存和记忆. 这种算法有种特点:如果数据是储存在磁盘或者磁 ...

  7. 异步任务-springboot

    异步任务-springboot 异步:异步与同步相对,当一个异步过程调用发出后,调用者在没有得到结果之前,就可以继续执行后续操作.也就是说无论异步方法执行代码需要多长时间,跟主线程没有任何影响,主线程 ...

  8. 帝国cms插件 解决后台修改信息时内容关键字不替换的问题

    很多站长是不是发现了帝国cms增加信息时,是有关键词替换的,这样是有利于网站优化排名. 但是在后台格式化数据之后,再去进行修改之后,对不起,内容关键字就实效了. 针对这一问题,解决方案如下: 找到 / ...

  9. 面试官:RabbitMQ有哪些工作模式?

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 今天又.又.又来面试了,还是老规 ...

  10. C++篇:第八章_类_知识点大全

    C++篇为本人学C++时所做笔记(特别是疑难杂点),全是硬货,虽然看着枯燥但会让你收益颇丰,可用作学习C++的一大利器 八.类 (一)类的概念与规则 "子类"和"子类型& ...