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*/
嗯,就是这样
- BZOJ 1264 浅谈数据结构优化特殊形式LCS动态规划求法
- BZOJ 1597 浅谈构造斜率--优化动态规划转移
- HDU 4719 浅谈数据结构优化双关键字动态规划
- CodeVS 2245 浅谈二维线段树优化间距限制型LCS动态规划状态转移
- 浅谈n阶汉诺伊塔问题拓展动态规划求法
- 各种子序列的动态规划求法与优化
- BZOJ 1833 浅谈简单数位动态规划
- BZOJ 1026 浅谈数位动态规划朝花夕拾
- BZOJ 2006 浅谈数据结构优化贪心思路
- 动态规划之LCS
- 动态规划 LCS
- 动态规划中LCS
- 动态规划 LCS,LIS
- 动态规划--LCS
- 动态规划:LCS
- 动态规划--LCS计算
- 动态规划(三.LCS)
- 动态规划-LCS
- 单例模式
- 关于函数返回值的几种情况
- Python ImportError: cannot import name NUMPY_MKL
- week1
- python中if __name__='__main__':的解析
- BZOJ 1264 浅谈数据结构优化特殊形式LCS动态规划求法
- JZOJsenior3366.【NOI2012】随机数生成器
- 二叉树遍历算法
- TCP:为什么是三次握手
- POJ3751 时间日期格式转换【日期计算】
- C# 类的单例模式
- NGUI实现背包效果
- 高斯核函数SVM TensorFlow实现
- Python 面向对象编程