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


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

    输入格式

    第一行输入两个整数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

    具体:

    定义状态 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 }

    -

  • 相关阅读:
    【bzoj3566】[SHOI2014]概率充电器 树形概率dp
    【bzoj1419】Red is good 期望dp
    【bzoj2698】染色 期望
    【bzoj2134】单选错位 期望
    【bzoj1022】[SHOI2008]小约翰的游戏John 博弈论
    【bzoj3170】[Tjoi 2013]松鼠聚会 旋转坐标系
    【bzoj2338】[HNOI2011]数矩形 计算几何
    【bzoj2085】[Poi2010]Hamsters Hash+倍增Floyd
    【bzoj1014】[JSOI2008]火星人prefix Splay+Hash+二分
    【bzoj2795】[Poi2012]A Horrible Poem Hash+分解质因数
  • 原文地址:https://www.cnblogs.com/jiamian/p/12207971.html
Copyright © 2020-2023  润新知