【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整数(占 ...
随机推荐
- 有关placeholder在ie9中的一点折腾
有关placeholder在ie9中的一点折腾. placeholder属性定义: placeholder 属性规定可描述输入字段预期值的简短的提示信息(比如:一个样本值或者预期格式的短描述). 问题 ...
- java继承时能包括静态的变量和方法吗?举例说明!
子类继承了超类定义的所有实例变量和方法包括静态的变量和方法(马克-to-win见下例),并且为它自己增添了独特的元素.子类只能有一个超类.Java不支持多超类的继承. 子类拥有超类的所有成员,但它不能 ...
- String和int、long、double等基本数据类型的转换
学习目标: 掌握Java的基本数据类型与String的转换 学习内容: 1.转化规则 String 转 基本数据类型 基本数据类型 变量 = 包装类.Parse基本数据类型(String); // c ...
- Java在方法中定义可变参数类型
学习目标: 掌握可变参数的应用 学习内容: 1.定义 在方法中传递数组有一种更简单的方式--方法的可变参数,其本质是一个语法糖,目的是让开发者写代码更简单. 2.语法 [修饰符] 返回值类型 方法名称 ...
- vivo 短视频推荐去重服务的设计实践
一.概述 1.1 业务背景 vivo短视频在视频推荐时需要对用户已经看过的视频进行过滤去重,避免给用户重复推荐同一个视频影响体验.在一次推荐请求处理流程中,会基于用户兴趣进行视频召回,大约召回2000 ...
- 第一阶段:Java基础之控制结构
1.顺序结构 按照顺序控制结构运行,即语句从上到下,从左到右 2.选择结构 if..else..语句 switch..case..语句 3.循环结构 while循环 do...while & ...
- Python 读取UCI iris数据集分析、numpy基础学习
python基础.numpy使用.io读取数据集.数据处理转换与简单分析.读取UCI iris数据集中鸢尾花的萼片.花瓣长度数据,进行数据清理,去重,排序,并求出和.累积和.均值.标准差.方差.最大值 ...
- VisualStudio安装步骤
1.下载vs2017,点击安装 2.选择asp.net选项进行安装,如果需要其他的功能,可以选上 3.更改安装路径,尽量把文件安装在c盘以外的盘上,因为c盘是系统盘,安装的东西越多电脑会越卡.注意:不 ...
- Jx.Cms开发笔记(二)-系统登录
界面 此界面完全抄了BootstrapAdmin css隔离 由于登录页面的css与其他页面没有什么关系,所以为了防止其他界面的css被污染,我们需要使用css隔离. css隔离需要在_Host.cs ...
- 黑客入门——渗透必备神器Burpsuit的安装和简单使用教程
很多人没有听说过burp全称(BurpSuite)BurpSuite是一款白帽子,黑帽子渗透测试必备工具,通过拦截HTTP/HTTPS的web数据包,当浏览器和相关应用程序的中间人,进行拦截.修改 ...