BZOJ 1264 浅谈数据结构优化特殊形式LCS动态规划求法

34 篇文章 0 订阅
12 篇文章 0 订阅
博客探讨了一种特殊形式的最长公共子序列(LCS)问题,利用题目中每个数字仅出现5次的特性,通过优化动态规划算法以避免平方复杂度。文章详细分析了动态规划方程,并提出利用数据结构优化转移过程,从而解决最大匹配问题。
摘要由CSDN通过智能技术生成

这里写图片描述
世界真的很大
DP复习中顺便搞一下数据结构
但这道题其实不是非常典型,并不是一般的DP,只是思路巧妙罢了
代码不是很难
算是复习一下LCS的DP求法吧,毕竟学了这么久了

看题先:

description:

基因匹配(match) 卡卡昨天晚上做梦梦见他和可可来到了另外一个星球,这个星球上生物的DNA序列由无数种碱基排列而成(地球上只有4种),而更奇怪的是,组成DNA序列的每一种碱基在该序列中正好出现5次!这样如果一个DNA序列有N种不同的碱基构成,那么它的长度一定是5N。 卡卡醒来后向可可叙述了这个奇怪的梦,而可可这些日子正在研究生物信息学中的基因匹配问题,于是他决定为这个奇怪星球上的生物写一个简单的DNA匹配程序。 为了描述基因匹配的原理,我们需要先定义子序列的概念:若从一个DNA序列(字符串)s中任意抽取一些碱基(字符),将它们仍按在s中的顺序排列成一个新串u,则称u是s的一个子序列。对于两个DNA序列s1和s2,如果存在一个序列u同时成为s1和s2的子序列,则称u是s1和s2的公共子序列。 卡卡已知两个DNA序列s1和s2,求s1和s2的最大匹配就是指s1和s2最长公共子序列的长度。 [任务] 编写一个程序:  从输入文件中读入两个等长的DNA序列;  计算它们的最大匹配;  向输出文件打印你得到的结果。

input:

输入文件中第一行有一个整数N,表示这个星球上某种生物使用了N种不同的碱基,以后将它们编号为1…N的整数。 以下还有两行,每行描述一个DNA序列:包含5N个1…N的整数,且每一个整数在对应的序列中正好出现5次。

output:

输出文件中只有一个整数,即两个DNA序列的最大匹配数目


首先还是解决一下LCS是何物
不是最长公共子串,而是最长公共子序列
这个东西我们一般都是有n^2的DP求法的。
如下:

#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
int f[1010][1010];
char s1[1010],s2[1010];
int main()
{
    while(scanf("%s%s",s1,s2)!=EOF)
    {
        int l1=strlen(s1),l2=strlen(s2);
        for(int i=1;i<=l1;i++)
            for(int j=1;j<=l2;j++)
            if(s1[i-1] == s2[j-1])
                f[i][j]=f[i-1][j-1]+1;
            else
                f[i][j]=max(f[i-1][j],f[i][j-1]);
        printf("%d\n",f[l1][l2]);
        memset(f,0,sizeof(f));
    }
}

但是呢,这道题我们看一下数据范围就是1e5,n^2不可做。
但是明确一点,单单LCS的话是没有比n^2更优的做法了
那么很显然肯定要利用题目性质
那么自然,题目中说“每个数只出现5次“会映入眼帘

分析一下上述LCS的动态规划方程,LCSdp方程的值的改变只是会出现在 s1[i] == s2[j]的地方,而其余情况都是直接由之前状态转移得来

那么,考虑b数组的每一个位置,其能更新的答案只有与其相同的数字的a数组的答案而已,其余的递推转移都只是。。无操作

但在一般的LCS里,我们无法得知与这个位置相同的a数组是哪些位置,所以需要去for
但反观这道题,每个数只有5个,那么对于每个数只会出现5次,我们就可以记录这些出现的位置,然后每一个b都只是去更新这些位置的答案

所以说我们的思路就出来了,对于找到的每一个b,我们找到其在a中的所有位置,记f(i)表示到a的第i位为止的LCS,然后找到其前面所有的f的最大值+1来更新答案。

即使我们都到了这一步,发现要想用前面的最大值来更新答案竟然还是n^2的。。。
现在的情况是,f(i)的答案由其前面所有的f值的最值得到,也就是所谓的”填表法“,这时我们就可以考虑用数据结构优化转移,而数据结构能有化的,也只有”转移“,而没有“更新”

还有需要注意的地方
枚举位置注意倒着枚举,从5到1。
因为当前bi只能转移一个位置,而如果先枚举前面的就很有可能使得后面枚举查询时最值改变

完整代码:

#include<stdio.h>
#include<algorithm>
using namespace std;

struct node
{
    int sum;
    node *ls,*rs;
    void update()
    {
        sum=max(ls->sum,rs->sum);
    }
}pool[2000010],*tail=pool,*root;

int n,pos[100010][6],top[100010],f[100010],ans=0;

node *build(int lf,int rg)
{
    node *nd=++tail;
    if(lf==rg)
    {
        nd->sum=0;
        nd->ls=nd->rs=0;
        return nd;
    }
    int mid=(lf+rg)>>1;
    nd->ls=build(lf,mid);
    nd->rs=build(mid+1,rg);
    return nd;
}

void modify(node *nd,int lf,int rg,int pos,int delta)
{
    if(lf==rg)
    {
        nd->sum=delta;
        return ;
    }
    int mid=(lf+rg)>>1;
    if(pos<=mid) modify(nd->ls,lf,mid,pos,delta);
    else modify(nd->rs,mid+1,rg,pos,delta);
    nd->update();
}

int query(node *nd,int lf,int rg,int L,int R)
{
    if(L<=lf && rg<=R)
        return nd->sum;
    int mid=(lf+rg)>>1,rt=0;
    if(L<=mid) rt=max(rt,query(nd->ls,lf,mid,L,R));
    if(R>mid) rt=max(rt,query(nd->rs,mid+1,rg,L,R));
    return rt;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=5*n;i++)
    {
        int x;
        scanf("%d",&x);
        pos[x][++top[x]]=i;
    }
    root=build(0,5*n);
    for(int i=1;i<=5*n;i++)
    {
        int x;
        scanf("%d",&x);
        for(int j=5;j>=1;j--)
        {
            int k=pos[x][j];
            f[k]=max(f[k],query(root,0,5*n,0,k-1)+1);
            modify(root,0,5*n,k,f[k]);
            ans=max(ans,f[k]);
        }
    }
    printf("%d\n",ans);
    return 0;
}
/*
EL PSY CONGROO
*/

嗯,就是这样

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值