[DP] DNA(基因重组) (DNA)

题目描述 Description

Juicepry®是一个世界著名的实验室。目前,实验室的科学家们正致力于对生物基因的重组进行深入研究。基因的物质载体是脱氧核糖核酸(DNA)。DNA是一种仅由A、T、G、C四种基元构成的双螺旋结构的有机分子。
DNA的两条单链上,同一位置的两个基元是互相对应的。A对T,G对C,因此,我们只需用任意一条链上的基元排列,就可以表示DNA的分子结构。例如:ATTGAGCCGTAT。
由于DNA微小而复杂,重组DNA极其困难,科学家们打算利用一条现成的DNA链作原材料拼接成另外一条新的DNA链。即使这样,拼接DNA仍然是一件繁重的工作,非人力所能胜任。所以科学家们制造了一种手术机器人TuringM来完成这项任务。TuringM每次只能在目标链( T T T)的右端与原材料 ( S S S) 的左端进行操作。它有下列几种基本拼接操作:
这里写图片描述
对于每种操作,机器人的单位时间耗费如上表所示(单位:分钟)。最后剩余的原材料自动丢弃。
现在的任务是请你编一个程序,帮助科学家们找出完成DNA链拼接的最少时间。

输入 Input

输入文件包括三行,第一行是三个数 c 1 、 c 2 、 c 3 c_1、c_2、c_3 c1c2c3
二、三两行每行一个个字符串,分别表示原材料DNA与目标DNA。

输出 Output

输出文件只有一行,表示拼接的最小时间。

样例输入 Sample Input

3 2 3
CCGATGTATCTG
TACGATCGGTC

样例输出 Sample Output

23

限制 Limits

数据限制:DNA链的长度不超过 5000 5000 5000;最少时间不会超过 10 10 10天= 1440 1440 1440分钟
Time Limit : 1 s 1s 1s & Memory Limit : 128 M B 128MB 128MB

剧透一下,这还是一道坑爹题
首先,刻画最优子结构:对于原串 S = s 1 s 2 … s n S=s_1s_2 …s_n S=s1s2sn与目标串 T = t 1 t 2 … t m T=t_1t_2 …t_m T=t1t2tm ,设其最优操作序列为 I 1 , I 2 , … , I p I_1,I_2,\ldots ,I_p I1,I2,,Ip 。第 i i i次操作后,原串与目标串分别是 S i S_i Si T i T_i Ti,显然有: S 0 = S , T 0 = ∅ S_0=S,T_0=\emptyset S0=S,T0= S p = ? S_p =? Sp=? T p = T T_p=T Tp=T。每次只能在目标链( T T T)的右端与原材料 ( S S S) 的左端进行操作。任取 S i , T i S_i ,T_i Si,Ti 作起始状态, S j 、 T j S_j 、T_j SjTj 作终止状态 ( i ≤ j ) (i\le j) (ij),操作序列 I i + 1 , … , I j I_{i+1} ,\ldots ,I_j Ii+1,,Ij,必然是这个子问题的最优解。以上述最优子结构为基础,容易设计出朴素的算法。
定义 d [ i , j ] d[i,j] d[i,j]表示由初始状态 S 0 , T 0 S_0,T_0 S0T0到达 S i , T i S_i,T_i SiTi的最少时间,可得递归方程式:
这里写图片描述
这种动态规划方法递推计算每一个状态需要 O ( n ) O(n) O(n)的时间,有 m ∗ n m*n mn个状态,因此总的时间复杂度为 O ( m n 2 ) O(mn^2 ) O(mn2)。但由于 m m m n n n的范围为 0 − 5000 0-5000 05000,这个复杂度仍然让我们难以接受。所以仍有优化空间。我们发现用细化状态转移、增加隐含状态的方法可以提高效率。为了将以此设计多个基元的操作分解成多次进行,而且不至于产生后效性,我们增加状态参数 k k k来描述达到此状态的最后一次操作的类型。

定义: d [ i , j , 0 ] d[i, j, 0] d[i,j,0]表示使用任意方法达到状态 d [ i , j ] d[i,j] d[i,j]的最少时间, d [ i , j , 1 ] d[i,j,1] d[i,j,1] d [ i , j , 2 ] d[i,j,2] d[i,j,2]表示最后一次使用直接copy和翻转copy达到状态 d [ i , j ] d[i,j] d[i,j]的最少时间, d [ i , j , 3 ] d[i,j,3] d[i,j,3]表示最后一次使用cut操作达到状态的最少时间。递归方程为:( d [ i , j ] d[i,j] d[i,j]表示意义化为 T i , S j T_i ,S_j Ti,Sj )
这里写图片描述
新算法的状态总量是 4 m n 4mn 4mn,但是计算每个状态的费用却降低到 O ( 1 ) O(1) O(1)。使
得整个算法时间复杂度降低了一次方。
我们还可以采用双阶段滚筒式方法优化问题的空间需求。
But!!!
No.1 题解没告诉初值…
No.2 实际写起来暴力还比滚动数组快?
真的,我服了…
先是滚动数组(用指针滚动,如果像HLOI2015 Color题一样用& 1 1 1滚动的话总有一个点WA,而且答案多一QAQ)

#include <cstdio>
#include <cstring>
#include <climits>
#include <algorithm>
#define MAXN 5010
using namespace std;

int c1,c2,c3,n,m;
int ans=INT_MAX;
int **dp1,**dp2;
char s[MAXN],t[MAXN];

int mymin(int a,int b)
{
	return a<b?a:b;
}

int mymin4(int a,int b,int c,int d)
{
	return mymin(mymin(a,b),mymin(c,d));
}

char match(char a)
{
	if (a=='A') return 'T';
	if (a=='G') return 'C';
	if (a=='C') return 'G';
	if (a=='T') return 'A';
}

int main()
{
	scanf("%d %d %d",&c1,&c2,&c3);
	scanf("%s",s);scanf("%s",t);
	n=strlen(s);m=strlen(t);
	dp1=new int*[n+1];
	dp2=new int*[n+1];
    for (int j=0;j<=n;j++)
    {
        dp1[j]=new int[4];dp2[j]=new int[4];
        dp1[j][0]=c2;
        dp1[j][1]=INT_MAX;
        dp1[j][2]=INT_MAX;
        dp1[j][3]=c2;
    }
    dp1[0][0]=0;dp1[0][3]=INT_MAX;
	for (int i=1;i<=m;i++)
    {
        dp2[0][0]=c3*i;
        dp2[0][1]=dp2[0][2]=dp2[0][3]=INT_MAX;
        for (int j=1;j<=n;j++)
		{
			dp2[j][1]=dp2[j][2]=INT_MAX;
			if (s[j-1]==t[i-1])
                dp2[j][1]=mymin(dp1[j-1][1],dp1[j-1][0]+c1);
            if (s[j-1]==match(t[i-1]))
                dp2[j][2]=mymin(dp1[j-1][2],dp1[j-1][0]+c1);
            dp2[j][3]=mymin(dp2[j-1][3],dp2[j-1][0]+c2);
            dp2[j][0]=mymin4(dp2[j][1],dp2[j][2],dp2[j][3],dp1[j][0]+c3);
		}
		swap(dp1,dp2);
    }
	for (int i=1;i<=n;i++)
        ans=mymin(ans,dp1[i][0]);
	printf("%d\n",ans);
	return 0;
}

下面是暴力
Code

一脸懵逼…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值