算法学习:LIS与LCS
1.LIS
- 状态定义:
f[i] 表示第i 个数(第i 个数记为a_i )结尾的LIS 的长度 - 初状态:
f[0] = 0 - 末状态:
max \{ f[i] \} (1 \le i \le n) - 状态转移方程:
f[i] = max \{ f[j]\} +1 (0 \le j < i,a_j < a_i)
(是不是非常好理解呢)
初状态是显然的;末状态是任意数都可以作为结尾,只比较长度;转移方程每一个状态是从所有前一个状态中最大值加上
代码咕咕咕(大雾
显然,这样的复杂度是不够优秀的,所以就有了一部分的优化。我们观察状态转移方程,发现需要我们计算前(我才不会说是我不会)
然而事实上还有更优的做法,用二分(
这个做法显然如果暴力查找,复杂度仍然是(这个同样自行百度233) 复杂度可以降低到
但是如果有兴趣自行举例,可以发现(正确性自行证明吧)
咕咕咕的代码如下
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100010;
int N;
int a[MAXN],b[MAXN];
int p;
int main() {
scanf("%d",&N);
for (register int i = 1; i <= N; i++) {
scanf("%d",&a[i]);
}
for (register int i = 1; i <= N; i++) {
if (a[i] > b[p]) {
b[++p] = a[i];
}
else {
b[lower_bound(b + 1,b + p + 1,a[i]) - b] = a[i];
}
}
printf("%d",p);
return 0;
}
例题(模板)
2.LCS
咕咕咕咕咕咕咕咕咕咕咕
- 状态定义:
f[i][j] 表示a_1 到a_i 和b_1 到b_j 的LCS - 初状态:
f[i][0]=f[0][j] =0(0 \le i \le |a|,0 \le j \le |b|) 其中|a|,|b| 表示a,b 数组的长度 - 末状态:
f[|a|][|b|] - 状态转移方程:
f[i][j] = max \{ f[i][j - 1],f[i -1][j],f[i-1][j-1] +1(a_i = b_j)\}
同样非常好理解吧
注意一下
正如刚才所说
3 1 2 4 5
1 4 5 2 3
我们对着上面一行对下面进行离散化(不会的去面壁百度)
得到
2 4 5 3 1
也就是每一位存的是第二行这一位在第一行对应的位置,然后我们通过我们某种巧妙的玄学方法看出来,这里相当于求一个因为我不会证明,反正)
然后我们做一遍
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100010;
int N;
int a[MAXN],b[MAXN],c[MAXN],d[MAXN];
int f[MAXN],p = 0;
int main() {
scanf("%d",&N);
for (register int i = 1; i <= N; i++) {
scanf("%d",&a[i]);
}
for (register int i = 1; i <= N; i++) {
scanf("%d",&b[i]);
}
for (register int i = 1; i <= N; i++) {
c[a[i]] = i;
}
for (register int i = 1; i <= N; i++) {
d[i] = c[b[i]];
}
for (register int i = 1; i <= N; i++) {
if (d[i] > f[p]) {
f[++p] = d[i];
}
else{
f[lower_bound(f + 1,f + p + 1,d[i]) - f] = d[i];
}
}
printf("%d",p);
return 0;
}
例题模板
完结撒花!!