C语言穷举法最长公共子序列,最长公共子序列问题

Biological applications often need to compare the DNA of two (or more) different organisms. A strand of DNA consists of a string of molecules called bases, where the possible bases are adenine, guanine, cytosine, and thymine. Representing each of these bases by its initial letter, we can express a strand of DNA as a string over the finite set {A,C,G,T}.

这是生物学应用中最为常见的“最长公共子序列问题”。

动态规划原理

能够用动态规划解决的问题,通常具有两种属性:第一,存在最优子结构,即可以用“剪切,粘帖”的方法来证明;第二,具有重叠子问题。

递归和穷举算法分析

在矩阵链乘法的问题中,可以使用递归和穷举两种方法来解决最优的括号化方案。其中,对每一种方案计算乘法运算次数。

用穷举法的时候,表中的每一个节点都必须访问,记访问一个节点的时间为$b_k$

则有递推式:

$b_n= sum_{k=0}^{n-1}b_kb_{n-1-k}$

该递推式符合卡特兰数,可以推出通项公式为:

$b(n)=frac{4^n}{sqrt{pi}n^{3/2}}(1+O(1/n))$

阶是关于n的指数函数。

用Recursive-Matrix-Chain递归求解,递推式符合

$T(n)geq1+sum_{k=1}^{n-1}(T(k)+T(n-k)+1) qquad n>1$

可以求得递推公式为:

$T(n)geq2^{n-1}$

可见朴素递归算法比穷举好一些。

动态规划问题和分治法区别

在merge-sort过程中,可以发现,问题不具有重叠字问题的性质,如下图所示,所有的子问题均不重叠。

C语言穷举法最长公共子序列,最长公共子序列问题_第1张图片

最大化矩阵括号方案

最大化矩阵括号化方案,问题仍然具有最优子结构性质。因为“剪切,粘帖”的处理方式没有变。

假设最大括号化方案能够使结果最优,则其中的划分为:

$A_1A_2dots dots A_kA_{k+1}dots A_j$

如果该方案不是最优的,总可以找到另外一种方案如下:

$A_1A_2dots A_iA_{i+1}dots dots A_j$

来替代该方案,使得结果最优。

贪心和动态规划的区别

贪心算法的原理是:我们不必计算原问题的最优解,总是可以在求解子问题的时候划分出$A_iA_{i+1}dots A_j$,选定的k使得$p_{i-1}p_{k}p_{j}$最小。这样的贪心算法并不能够得到最优解。原因如下:

C语言穷举法最长公共子序列,最长公共子序列问题_第2张图片

虽然我们求出来的最优解如红色的图,但很有可能

$p_{u-1}p_wp_v

原因如下:最优解的函数表达式与贪心中的$p_k$函数表达式不同,所以贪心算法并不能够用于最优解问题。

编程验证:

贪心策略如下:

//下面这是用错误的贪心算法求解的问题:

Matrix_Chain Matrix_Chain_Order_greedy(int p[])

{

int N=n-1;

Matrix_Chain T_greedy;

for(int i=0;i

T_greedy.m[i][i]=0;

for(int l=2;l<=N;l++)

{

for(int i=1;i<=N-l+1;i++)

{

int j=i+l-1;

T_greedy.m[i-1][j-1]=INFINITY;

int q_min=INFINITY;

for(int k=i;k<=j-1;k++)

{

int q=T_greedy.m[i-1][k-1]+T_greedy.m[k][j-1]+p[i-1]*p[k]*p[j];

if(q_min>p[i-1]*p[k]*p[j])

{

q_min=p[i-1]*p[k]*p[j];

T_greedy.m[i-1][j-1]=q;

T_greedy.s[i-1][j-1]=k-1;

}

}

}

}

return T_greedy;

}

结果如图:

C语言穷举法最长公共子序列,最长公共子序列问题_第3张图片

C语言穷举法最长公共子序列,最长公共子序列问题_第4张图片

C语言穷举法最长公共子序列,最长公共子序列问题_第5张图片

很显然地看出,greedy_result得到了不同的括号化结果。

钢条切割问题的限制

C语言穷举法最长公共子序列,最长公共子序列问题_第6张图片

如果我们限制,在r=4的时候,只能够切割成两段长度为1的钢条,则r=4的时候,最优切割方案1+1+1+1无法成立。

外币兑换问题

该问题可以看成是一种矩阵链乘法的变形,当佣金$C_k$为任意值的时候,并不符合最优子结构的性质。

具体的描述见下图:

C语言穷举法最长公共子序列,最长公共子序列问题_第7张图片

合并问题的代价,可以描述为$C_k$

解决每个问题所需要的代价为$R_k$

总代价的递推式为:

$R_k+C_k$

$R_{k2}+C_{k_2}$

如果$C_k$不为常数,假设它可以用一个$f(k)$描述,则问题的代价可以描述为:

$R_k+f(k)$

动态规划算法只能保证$R_k$的最优解,并不能保证$f(k)$的最优解。

最长公共子序列

最长公共子序列的求解,如下图所示:

C语言穷举法最长公共子序列,最长公共子序列问题_第8张图片

可以看到,最长公共子序列的依赖关系如上图所示,[i,j]的值取决于{[i-1,j-1],[i,j-1],[i-1,j]}

实现方法如下:

LCS_len.h

#include

#include

#include

#define M 6

#define N 7

wchar_t b[N+1][M+1]={'S'}; //表示起点start

int c[N+1][M+1]={0};

wchar_t northwest=L'\\', up=L'|', leftway=L'-';

void LCS_length(char *x,char *y)

{

for(int i1=0;i1<=N;i1++)

b[i1][0]='S';

for(int j1=0;j1<=M;j1++)

b[0][j1]='S';

for(int i=1;i<=N;i++)

{

for(int j=1;j<=M;j++)

{

if(x[i]==y[j])

{

c[i][j]=c[i-1][j-1]+1;

b[i][j]=northwest; //Northwest往左上

}

else

{

if(c[i-1][j]>=c[i][j-1])

//c[i-1][j-1] 过渡到 c[i][j],需要将c[i-1][j]和c[i][j-1]比较大小

//取较大的那一个值

{

c[i][j]=c[i-1][j];

b[i][j]=up; //Up往上

你可能感兴趣的:(C语言穷举法最长公共子序列)