蒜头君的日志(最长公共上升子序列)

蒜头君喜欢把做过的事情记录下来,写在日志里,为了安全起见,它还有一份备份放在另外的地方,不过很不幸的是,最近他的两份日志都受到了破坏,有增加删除修改,但没有改变任何原来的顺序,两份受到的破坏不一定一样,蒜头君记录事情都是按时间顺序的,记录的也都是时间戳,所以正确的记录上时间戳肯定是严格递增的,并且只有两份记录上都出现了的时间戳他才认为有可能自己做了,现在他想知道他最多可能做了多少事情。

输入格式

第一行输入两个整数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]表示字符串ai结尾字符串bj结尾且最长公共上升子序列最后一位为b[j]的答案。

如果 a[i] != b[j],那么这个答案就是dp[i - 1][j],否则需要从前边找最大的dp[i - 1][k]并且满足b[k] < b[j]的加1,这个可以在循环中预先找到,不用每一次都找k

具体:

扫描二维码关注公众号,回复: 8673168 查看本文章

定义状态 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\n",ans);
44     return 0;
45 }

-

猜你喜欢

转载自www.cnblogs.com/jiamian/p/12207971.html