题目描述 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 c1、c2、c3。
二、三两行每行一个个字符串,分别表示原材料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=s1s2…sn与目标串
T
=
t
1
t
2
…
t
m
T=t_1t_2 …t_m
T=t1t2…tm ,设其最优操作序列为
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
Sj、Tj 作终止状态
(
i
≤
j
)
(i\le j)
(i≤j),操作序列
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
S0,T0到达
S
i
,
T
i
S_i,T_i
Si,Ti的最少时间,可得递归方程式:
这种动态规划方法递推计算每一个状态需要
O
(
n
)
O(n)
O(n)的时间,有
m
∗
n
m*n
m∗n个状态,因此总的时间复杂度为
O
(
m
n
2
)
O(mn^2 )
O(mn2)。但由于
m
m
m、
n
n
n的范围为
0
−
5000
0-5000
0−5000,这个复杂度仍然让我们难以接受。所以仍有优化空间。我们发现用细化状态转移、增加隐含状态的方法可以提高效率。为了将以此设计多个基元的操作分解成多次进行,而且不至于产生后效性,我们增加状态参数
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
一脸懵逼…