From 8dc6dcbb9c61b5db5adc6c435d96e89bb879de5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 6 Mar 2024 15:28:06 +0800 Subject: [PATCH] 'commit' --- TangDou/AcWing_TiGao/T1/LIS/272_1.cpp | 2 +- TangDou/AcWing_TiGao/T1/LIS/272_2.cpp | 6 +++--- TangDou/AcWing_TiGao/T1/LIS/LIS+LCS专题.md | 13 ++++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/TangDou/AcWing_TiGao/T1/LIS/272_1.cpp b/TangDou/AcWing_TiGao/T1/LIS/272_1.cpp index 3dddf6f..af9888f 100644 --- a/TangDou/AcWing_TiGao/T1/LIS/272_1.cpp +++ b/TangDou/AcWing_TiGao/T1/LIS/272_1.cpp @@ -24,7 +24,7 @@ int main() { int mx = 1; // 最起码a[i]==b[j],有一个数字是一样嘀~ // f[i-1]是肯定的了,问题是b的前驱在哪里?需要枚举1~j-1 for (int k = 1; k < j; k++) - if (b[j] > b[k]) // j可以接在k后面,那么可能的最大值为f[i-1][k]+1 + if (a[i] > b[k]) // j可以接在k后面,那么可能的最大值为f[i-1][k]+1 mx = max(mx, f[i - 1][k] + 1); // 更新答案 f[i][j] = max(f[i][j], mx); diff --git a/TangDou/AcWing_TiGao/T1/LIS/272_2.cpp b/TangDou/AcWing_TiGao/T1/LIS/272_2.cpp index 97a0eab..b2b4152 100644 --- a/TangDou/AcWing_TiGao/T1/LIS/272_2.cpp +++ b/TangDou/AcWing_TiGao/T1/LIS/272_2.cpp @@ -7,6 +7,7 @@ int f[N][N]; int res; // O(n^2) +// f[i][j]—集合:考虑 a 中前 i 个数字,b 中前 j 个数字 ,且当前以 b[j] 结尾的子序列的方案 int main() { int n; cin >> n; @@ -14,10 +15,9 @@ int main() { for (int i = 1; i <= n; i++) cin >> b[i]; for (int i = 1; i <= n; i++) { - int mx = 1; // 将mx提到二层循环的外面,因为O(n^3)之所以慢,就是因为存在重复计算,每次都需要从头找一遍可以接在后面的同时上升序列最长的那个k - // 由于循环从左到右进行时,每次这个mx是可以记录下来,下次不用重复计算的,所以,可以复用 + int mx = 1; // 如果a[i]==b[j],那么LICS最小是1.如果下面的循环中没有一个if被执行,则mx没使上 for (int j = 1; j <= n; j++) { - f[i][j] = f[i - 1][j]; + f[i][j] = f[i - 1][j]; // 先继承过来,现实含义:即使a[i]!=b[j],那么最长长度不会因为i的增加而变化,即f[i][j]=f[i-1][j] if (a[i] == b[j]) f[i][j] = mx; if (a[i] > b[j]) mx = max(mx, f[i - 1][j] + 1); } diff --git a/TangDou/AcWing_TiGao/T1/LIS/LIS+LCS专题.md b/TangDou/AcWing_TiGao/T1/LIS/LIS+LCS专题.md index 2bb01ca..217347e 100644 --- a/TangDou/AcWing_TiGao/T1/LIS/LIS+LCS专题.md +++ b/TangDou/AcWing_TiGao/T1/LIS/LIS+LCS专题.md @@ -597,7 +597,7 @@ int main() { int mx = 1; // 最起码a[i]==b[j],有一个数字是一样嘀~ // f[i-1]是肯定的了,问题是b的前驱在哪里?需要枚举1~j-1 for (int k = 1; k < j; k++) - if (b[j] > b[k]) // j可以接在k后面,那么可能的最大值为f[i-1][k]+1 + if (a[i] > b[k]) // j可以接在k后面,那么可能的最大值为f[i-1][k]+1 mx = max(mx, f[i - 1][k] + 1); // 更新答案 f[i][j] = max(f[i][j], mx); @@ -610,8 +610,12 @@ int main() { return 0; } ``` +前面之所以把$b[j]$换成$a[i]$,是因为方便现在考虑。 + +由于在枚举计算$f[i][j]$时,却用到的是和内层循环(即$b[j]$)无关的,只和外层循环(即$a[i]$)有关,而我们在计算$f[i][j]$,每次都要用到前$j−1$个数中小于$a[i]$的$f[i-1][k]$最大值,所以我们可以用一个变量$mx$不断更新它来保存前$j−1$的答案,就可以避免重复计算,从而降低时间复杂度。 $O(N^2)$ + ```cpp {.line-numbers} #include @@ -622,6 +626,7 @@ int f[N][N]; int res; // O(n^2) +// f[i][j]—集合:考虑 a 中前 i 个数字,b 中前 j 个数字 ,且当前以 b[j] 结尾的子序列的方案 int main() { int n; cin >> n; @@ -629,10 +634,9 @@ int main() { for (int i = 1; i <= n; i++) cin >> b[i]; for (int i = 1; i <= n; i++) { - int mx = 1; // 将mx提到二层循环的外面,因为O(n^3)之所以慢,就是因为存在重复计算,每次都需要从头找一遍可以接在后面的同时上升序列最长的那个k - // 由于循环从左到右进行时,每次这个mx是可以记录下来,下次不用重复计算的,所以,可以复用 + int mx = 1; // 如果a[i]==b[j],那么LICS最小是1.如果下面的循环中没有一个if被执行,则mx没使上 for (int j = 1; j <= n; j++) { - f[i][j] = f[i - 1][j]; + f[i][j] = f[i - 1][j]; // 先继承过来,现实含义:即使a[i]!=b[j],那么最长长度不会因为i的增加而变化,即f[i][j]=f[i-1][j] if (a[i] == b[j]) f[i][j] = mx; if (a[i] > b[j]) mx = max(mx, f[i - 1][j] + 1); } @@ -643,6 +647,5 @@ int main() { return 0; } - ``` [最长上升子序列 ($LIS$) 详解+例题模板 (全)](https://blog.csdn.net/lxt_Lucia/article/details/81206439)