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

来源:互联网 发布:linux网卡驱动下载 编辑:程序博客网 时间:2024/05/20 19:29

这里写图片描述
世界真的很大
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*/

嗯,就是这样

原创粉丝点击