Day6
最长公共子序列
状态表示:
f
[
i
,
j
]
f[i, j]
f[i,j] 所有从
A
[
1.....
i
]
A[1.....i]
A[1.....i] 与
B
[
1.....
j
]
B[1.....j]
B[1.....j] 的公共子序列的集合
集
合
划
分
=
{
A
[
i
]
包
含
,
B
[
j
]
包
含
(
f
[
i
−
1
,
j
−
1
]
+
1
)
A
[
i
]
包
含
,
B
[
j
]
不
包
含
(
f
[
i
,
j
−
1
]
)
A
[
i
]
不
包
含
,
B
[
j
]
包
含
(
f
[
i
−
1
,
j
]
)
A
[
i
]
不
包
含
,
B
[
j
]
也
不
包
含
(
f
[
i
−
1
,
j
−
1
]
)
集合划分=\begin{cases} A[i]包含,B[j]包含 && (f[i - 1, j - 1] + 1)\\ A[i]包含,B[j]不包含 && (f[i, j - 1])\\ A[i]不包含,B[j]包含 && (f[i - 1, j]) \\ A[i]不包含,B[j]也不包含 &&(f[i - 1 , j - 1])\end{cases}
集合划分=⎩⎪⎪⎪⎨⎪⎪⎪⎧A[i]包含,B[j]包含A[i]包含,B[j]不包含A[i]不包含,B[j]包含A[i]不包含,B[j]也不包含(f[i−1,j−1]+1)(f[i,j−1])(f[i−1,j])(f[i−1,j−1])
所以可得状态转移方程:
f
[
i
,
j
]
=
m
a
x
(
f
[
i
−
1
,
j
−
1
]
+
1
,
f
[
i
,
j
−
1
]
,
f
[
i
−
1
,
j
]
,
f
[
i
−
1
,
j
−
1
]
)
f[i, j] = max(f[i - 1, j - 1] + 1, f[i, j - 1], f[i - 1, j], f[i - 1,j - 1])
f[i,j]=max(f[i−1,j−1]+1,f[i,j−1],f[i−1,j],f[i−1,j−1])
第二和第三个集合的状态表示是无法直接表示出来的,但是状态划分虽然一般是不充不漏的,但是我们是求最大值,所以可以重但是不能漏,所以第二和第三种状态表示只是包括了那两个集合,也包含了部分其他的,而恰好第四种情况其实已经在第二三种情况时计算过了,所以不用重复计算:
f
[
i
,
j
]
=
m
a
x
(
f
[
i
−
1
,
j
−
1
]
+
1
,
f
[
i
−
1
,
j
]
,
f
[
i
,
j
−
1
]
)
;
f[i, j] = max(f[i - 1, j - 1] + 1, f[i - 1, j], f[i , j - 1]);
f[i,j]=max(f[i−1,j−1]+1,f[i−1,j],f[i,j−1]);
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, m;
int f[N][N];
char a[N], b[N];
int main(void)
{
cin >> n >> m >> a + 1 >> b + 1; // 下标从1开始
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]);
// 如果此时a[i] = b[j]时,才存在第1种情况
if (a[i] == b[j]) f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
}
}
cout << f[n][m] << endl ;
return 0;
}
最长公共上升子序列
状态表示:
f
[
i
,
j
]
f[i, j]
f[i,j] 表示所有在
A
[
1......
i
]
A[1......i]
A[1......i] 中和
B
[
1......
j
]
B[1......j]
B[1......j] 中都出现过,且以
B
[
j
]
B[j]
B[j] 结尾的公共上升子序列的集合
集
合
划
分
=
{
不
包
含
A
[
i
]
的
公
共
子
序
列
(
f
[
i
−
1
,
j
]
)
包
含
A
[
i
]
的
公
共
子
序
列
(
m
a
x
(
f
[
i
−
1
,
1....
j
]
+
1
)
)
集合划分=\begin{cases} 不包含A[i]的公共子序列 & (f[i - 1, j]) \\ \\包含A[i]的公共子序列 &(max(f[i - 1, 1....j] + 1))\end{cases}
集合划分=⎩⎪⎨⎪⎧不包含A[i]的公共子序列包含A[i]的公共子序列(f[i−1,j])(max(f[i−1,1....j]+1))
不包含
A
[
i
]
A[i]
A[i] 的集合很好理解,跟具定义出发就是
f
[
i
−
1
,
j
]
f[i - 1, j]
f[i−1,j] ,包含
A
[
i
]
A[i]
A[i] 在什么情况下才存在呢?只有在
A
[
i
]
=
B
[
j
]
A[i] = B[j]
A[i]=B[j] 时才存在,因为是以
B
[
j
]
B[j]
B[j] 结尾,然后我们怎么去找这个集合中的最大,就是从
1
1
1 到
j
j
j 找一个max即可,朴素版是需要三层循环的,在枚举
1
1
1 到
j
j
j 时我们可以考虑前缀和的思想去用一个
m
a
x
v
maxv
maxv 去把每一次的遍历的最大值都存起来,这样就不用每一次都去遍历了减少了时间复杂度
#include<bits/stdc++.h>
using namespace std;
const int N = 3010;
int a[N], b[N], f[N][N], n;
int main(void)
{
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
for (int i = 1; i <= n; i++) {
int maxv = 1;
for (int j = 1; j <= n; j++) {
f[i][j] = f[i - 1][j]; // 先让f[i][j]等于左边集合
if (a[i] == b[j]) f[i][j] = max(maxv, f[i][j]); //如果右边集合存在,再次更新f[i][j]
if (b[j] < a[i]) maxv = max(maxv, f[i - 1][j] + 1); //如果当前值小于a[i],那就更新maxv
}
}
int res = 0;
for (int i = 1; i <= n; i++) res = max(res, f[n][i]);
//因为b[j]是一定被选,所以要枚举取最大值
cout << res << endl ;
return 0;
}
OK,准备回归背包问题!
明天叭,今天感觉什么都没干,就过去了,放点水题上来水水博客,比如AcWing周赛 [滑稽.jpg]
#include<bits/stdc++.h>
using namespace std;
int main(void)
{
int p1, p2, p3, p4, a, b;
if (a > b) swap(a, b);
cin >> p1 >> p2 >> p3 >> p4 >> a >> b;
int c = min(p1, min(p2, min(p3, p4))) - 1;
if (c < a) puts("0");
else if (a <= c && c <= b) printf("%d", c - a + 1);
else printf("%d", b - a + 1);
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int n ;
int res = 1;
void dfs(int x)
{
if (x * 10 <= n) {
res ++ ;
dfs(x * 10);
}
if (x * 10 + 1 <= n) {
res ++;
dfs(x * 10 + 1);
}
}
int main(void)
{
cin >> n;
dfs(1);
cout << res << endl ;
return 0;
}