蒜头君喜欢把做过的事情记录下来,写在日志里,为了安全起见,它还有一份备份放在另外的地方,不过很不幸的是,最近他的两份日志都受到了破坏,有增加删除修改,但没有改变任何原来的顺序,两份受到的破坏不一定一样,蒜头君记录事情都是按时间顺序的,记录的也都是时间戳,所以正确的记录上时间戳肯定是严格递增的,并且只有两份记录上都出现了的时间戳他才认为有可能自己做了,现在他想知道他最多可能做了多少事情。
输入格式
第一行输入两个整数n,m代表两份记录的长度。(1≤n,m≤103)
接下来一行输入n个整数,a1,a2,a3⋯an,代表第一份记录的n个时间戳。(1≤ai≤103)
接下来一行输入m个整数,b1,b2,b3⋯bm,代表第二份记录的m个时间戳。(1≤bi≤103)
输出格式
输出一个整数,代表蒜头君最多可能做了多少事情。
输入样例
3 2 1 3 2 1 2
输出样例
2
这道题是求最长公共上升子序列,结合 LCS 和 LIS 的思想,设 dp[i][j]
表示字符串a
以i
结尾字符串b
以j
结尾且最长公共上升子序列最后一位为b[j]
的答案。
如果 a[i] != b[j]
,那么这个答案就是dp[i - 1][j]
,否则需要从前边找最大的dp[i - 1][k]
并且满足b[k] < b[j]
的加1
,这个可以在循环中预先找到,不用每一次都找k
。
具体:
定义状态 F[i][j]表示以 a 串的前 i 个字符 b 串的前 j 个字符 且以b[j]为结尾构成的 LCIS 的长度。
这个状态依赖于哪些状态呢?
首先,在 a[i]!=b[j]的时候有F[i][j]=F[i-1][j] 。为什么呢?因为F[i][j]是以 b[j]为结尾的 LCIS, 如果F[i][j]>0 那么就说明a[1]..a[i] 中必然有一个字符a[k]等于b[j](如果F[i][j]等于 0呢?那 赋值与否都没有什么影响了) 。因为 a[k]!=a[i],那么a[i]对F[i][j]没有贡献,于是我们不考虑 它照样能得出 F[i][j]的最优值。所以在 a[i]!=b[j]的情况下必然有 F[i][j]=F[i-1][j] 。这一点参考LCS 的处理方法。
那如果a[i]==b[j]呢?首先,这个等于起码保证了长度为1 的 LCIS。然后我们还需要去找一 个最长的且能让b[j]接在其末尾的LCIS。之前最长的LCIS 在哪呢?首先我们要去找的F 数 组的第一维必然是i-1。因为 i 已经拿去和 b[j]配对去了,不能用了。并且也不能是i-2,因 为i-1 必然比i-2更优。第二维呢?那就需要枚举b[1]..b[j-1] 了,因为你不知道这里面哪个最 长且哪个小于b[j]。这里还有一个问题,可不可能不配对呢?也就是在a[i]==b[j]的情况下, 需不需要考虑F[i][j]=F[i-1][j] 的决策呢?答案是不需要。因为如果b[j]不和 a[i]配对,那就是 和之前的a[1]..a[j-1] 配对(假设F[i-1][j]>0,等于0 不考虑) ,这样必然没有和a[i]配对优越。 (为什么必然呢?因为b[j]和 a[i]配对之后的转移是max(F[i-1][k])+1 ,而和之前的i`配对则 是max(F[i`-1][k])+1 。显然有F[i][j]>F[i`][j],i`>i)
于是我们得出了状态转移方程:
a[i]!=b[j]: F[i][j]=F[i-1][j]
a[i]==b[j]: F[i][j]=max(F[i-1][k])+1 1<=k<=j-1&&b[j]>b[k]
不难看到,这是一个时间复杂度为O(n^3)的 DP,离平方还有一段距离。
但是,这个算法最关键的是,如果按照一个合理的递推顺序, max(F[i-1][k])的值我们可以在 之前访问 F[i][k]的时候通过维护更新一个 max 变量得到。
怎么得到呢?首先递推的顺序必 须是状态的第一维在外层循环, 第二维在内层循环。 也就是算好了F[1][len(b)]再去算F[2][1]。
如果按照这个递推顺序我们可以在每次外层循环的开始加上令一个max 变量为0,然后开始 内层循环。当 a[i]>b[j]的时候令 max=F[i-1][j] 。
如果循环到了 a[i]==b[j]的时候,则令 F[i][j]=max+1。 最后答案是F[len(a)][1]..F[len(a)][len(b)] 的最大值。
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <string> 5 #include <math.h> 6 #include <algorithm> 7 #include <vector> 8 #include <stack> 9 #include <queue> 10 #include <set> 11 #include <map> 12 #include <sstream> 13 const int INF=0x3f3f3f3f; 14 typedef long long LL; 15 const int maxn=1e5+10; 16 using namespace std; 17 18 int a[1005]; 19 int b[1005]; 20 int dp[1005][1005]; 21 22 int main() 23 { 24 int n,m; 25 scanf("%d %d",&n,&m); 26 for(int i=1;i<=n;i++) 27 scanf("%d",&a[i]); 28 for(int i=1;i<=m;i++) 29 scanf("%d",&b[i]); 30 for(int i=1;i<=n;i++) 31 { 32 int MAX=0; 33 for(int j=1;j<=m;j++) 34 { 35 dp[i][j]=dp[i-1][j]; 36 if(a[i]>b[j]&&MAX<dp[i-1][j]) MAX=dp[i-1][j]; 37 else if(a[i]==b[j]) dp[i][j]=MAX+1; 38 } 39 } 40 int ans=0; 41 for(int i=1;i<=m;i++) 42 ans=max(ans,dp[n][i]); 43 printf("%d ",ans); 44 return 0; 45 }
-