首页 > 其他分享 >浅谈LIS和LCS

浅谈LIS和LCS

时间:2022-10-11 14:56:56浏览次数:71  
标签:10 浅谈 int ans mp LIS 序列 inf LCS

这是一篇由超级菜的OIer写的博客......
image

LIS就是最长上升子序列,通过DP求解。
普通的求法是\(O({n}^{2})\)的(相信大家都会写):

解法1

#include <bits/stdc++.h>
using namespace std;
const int N = 105, inf = 1e9 + 10;
int a[N], f[N];
int n, ans = -inf;
int main() {
	cin >> n;
	for (int i = 1; i <= n; i ++) {cin >> a[i]; f[i] = 1;}
	for (int i = 1; i <= n; i ++)
		for (int j = 1; j < i; j ++)
			if (a[i] > a[j]) f[i] = max(f[i], f[j] + 1);
	for (int i = 1; i <= n; i ++)
		ans = max(ans, f[i]);
	cout << ans << '\n';
	return 0;
}

n如果比较大,这种做法当然就不行了。
有一种基于贪心的二分求解方法,我们维护一个最长上升序列,扫描a[i], a[i]>序列末尾的数,加进去,序列长度+1;如果a[i] <=序列末尾,我们可以通过二分找到序列中第一个大等于a[i]的数,用a[i]替换掉它(相当于在不改变序列单调性的前提下使某一个数减小),这就是基于贪心思想的求法,最后序列的长度也就是我们要找的LIS。

解法2

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, inf = 1e9 + 10;
int f[N], a[N];
int n, ans;
int main() {
	cin >> n;
	for (int i = 1; i <= n; i ++) cin >> a[i], f[i] = inf;
	f[1] = a[1];
	ans = 1;//初始答案
	for (int i = 2; i <= n; i ++) {
		if (a[i] > f[ans]) f[++ ans] = a[i];
		else f[lower_bound(f + 1, f + ans + 1, a[i]) - f] = a[i];
	}
	cout << ans << '\n'; 
}

这样复杂度就优化成\(O(nlogn)\)了。

还有没有其他做法呢?众所周知,算法的魅力有很大一部分在于寻找不断优化的过程,我们进一步分析DP过程,f[i] = max(f[j] + 1, f[i]) (a[j] < a[i], j < i),而我们每次更新f[i]时,都要枚举1~i-1的所有j,这是很大的浪费。我们可以用树状数组优化,
对于每个a[i],查询它前面最大的f[j],f[i] = f[j] + 1,再向树状数组a[i]位置中插入f[i],该树状数组以值域为下标,维护的是最大的f[j]。复杂度同样是\(O(nlogn)\), 如果数值较大,记得离散化。
image

解法3

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, c[N], f[N], a[N];
void add(int x, int y) {
	while (x < N) {
		c[x] = max(c[x], y);
		x += x & (-x);
	}
}
int query(int x) {
	int res = 0;
	while(x) {
		res = max(res, c[x]);
		x -= x & (-x);
	}
	return res;
}
signed main() {
	cin >> n;
	for (int i = 1; i <= n; i ++) cin >> a[i];
	for (int i = 1; i <= n; i ++) {
		f[i] = query(a[i]) + 1;
		add(a[i], f[i]);
	}
	int ans = 0;
	for (int i = 1; i <= n; i ++) ans = max(ans, f[i]);
	cout << ans << '\n';
}

LCS最长公共子序列。给你两个序列,求他们的最长公共部分。

常规解法:

#include <bits/stdc++.h>
using namespace std;
const int N = 5010;
int a[N], b[N], f[N][N], n, m;
int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i ++) cin >> a[i];
	for (int i = 1; i <= m; i ++) cin >> b[i];
	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j <= m; j ++) {
			f[i][j] = max(f[i - 1][j], f[i][j - 1]);
			if (a[i] == b[j]) f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
		}
	}
	cout << f[n][m];
}

n较大时这种解法肯定不能用了,此时题目应该还会给其他条件。
看一道题:
P1439 【模板】最长公共子序列
两个序列是相同长度n,且其中的元素范围都是1~n,观察n可以取到\(10 ^ 5\),那么上面的方法肯定不能用,我们可以将a序列每个元素映射为编号,这样a序列就是1~n,对于b采用a的映射方式,问题就转化为了求b的最长上升子序列了,采用二分的方式。
image

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, inf = 1e9 + 10;
int n, a[N], b[N], mp[N], f[N];
int main() {
	cin >> n;
	for (int i = 1; i <= n; i ++) {cin >> a[i]; mp[a[i]] = i;}
	for (int i = 1; i <= n; i ++) {cin >> b[i]; f[i] = inf;}
	int len = 0;
	for (int i = 1; i <= n; i ++) {
		if (mp[b[i]] > f[len]) f[++ len] = mp[b[i]];
		else f[lower_bound(f + 1, f + len + 1, mp[b[i]]) - f] = mp[b[i]];
		//找f数组中第一个>=mp[b[i]]的位置。 
	}
	cout << len << '\n';
	return 0;
}

标签:10,浅谈,int,ans,mp,LIS,序列,inf,LCS
From: https://www.cnblogs.com/YHxo/p/16779168.html

相关文章

  • WPF listbox中添加index
    关键代码:如果中在ItemsControl中加入Index,"RelativeSource={RelativeSourceAncestorType=ListBoxItem}"可以写成,"RelativeSource={RelativeSourceTemplatedParent}"但是......
  • WPF ListBox添加新数据时自动滚到最后一行
    Xaml文件代码如下:<ListBoxx:Name="lstBox"Height="200"AlternationCount="100000"ItemsSource="{BindingLogs}"><List......
  • Opera Browser -- Access Restricted Sites using Free VPN /Free VPN Services List
    OperaBrowser --AccessRestrictedSitesusingFreeVPN:currentlythefeatureisavailableinOperaDeveloperversiononly.h​​ttp://www.clickonf5.org/126016......
  • 浅谈一下山海鲸和镝数这2款软件
    “数据可视化”无疑是当下被讨论的最多的话题之一,我也经常在各个平台看到有人提及。近日,我在翻阅知乎有关于可视化工具时经常能够看到两个名字:“山海鲸可视化”和“镝数图......
  • Java 中初始化 List 的五种方法
    1、构造List后使用List.add初始化1List<String>stringList=newLinkedList<>();2stringList.add("a");3stringList.add("b");4stringList.add("c");这是......
  • 浅谈自定义事件如何创建,触发和监听?
    我们知道的原生js事件,即内置事件有click,blur,mousemove等等。那如果我们想自定义一个事件呢?1、通过newEvent创建一个事件实例2、触发事件通过dispatch进行事件分发3......
  • list集合的add和set方法区别
    JavaList.add添加元素java中list添加元素有2种方式,一种是add(Elemente),添加元素时,是依次往后添加;另一种是add(Indexi,Elemente),添加元素时,若索引位置没有值,则直接添加,若......
  • 浅谈Go1.18版本新特性——泛型
    泛型Generics:引入了对使用参数化类型的泛型代码的新支持,达到了算法可复用的目的。两数求和,泛型函数的使用假设我们要计算两个数的和,函数可以这样子写funcAdd(a......
  • java中列表 Not showing null elements 列表中去除null 使用 list.removeAll(Collec
    java中列表Notshowingnullelements列表中去除nullNotshowingnullelements有时候看见list的size与结果不一致,例如下面这样导致原因:list集合允许null值,......
  • 浅谈gedit配置
    \(\text{gedit}\)是\(\text{GNOME}\)桌面的小型和轻量文本编辑器因为之前看学长博客感觉燕大的电脑似乎比较[数据删除],于是去问了\(\text{CDsidi}\)大佬\(\text{v......