您的位置:首页 > 理论基础 > 数据结构算法

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

2017-09-27 20:42 337 查看


世界真的很大

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
*/


嗯,就是这样
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: