动态规划

如果一个问题由交叠的子问题所构成,可以采用动态规划的方法进行求解。

交叠的子问题可以理解为要想求解当前问题必须知道前一问题的解,也就是问题之间不是相互独立的。

动态规划一般分为以下几步进行:

1.找出最优解性质,刻画其结构特征;

2.递归定义最优解,写出动态规划方程;

3.以自底向上的方式计算出最优解;

4.根据计算得到的信息,构造最优解。

下面有一个LCS(最长公共子序列)的例子。

第一步找最优解性质,很明显子序列长度可以表征其最优解的性质。

第二步写出动态规划方程

公式编辑器那么大的括号,各位看官将就下,主要看内容。

len(i,j)   =    0           if i=0 or j=0;

len(i,j)  = len(i-1,j-1)    if i>0,j>0 and ai=bj

len(i,j)  = max(len(i,j-1),len(i-1,j))  if i>0,j>0 and ai!=bj

第三步求解最优解

#include<iostream>
#include<string>
#include<stack>
using namespace std;
stack<char> squence;
void getLCS(int **&tmp1,int **&tmp2,int len1,int len2,string sq1,string sq2){
	//两个矩阵  一个存长度  一个存方向
	tmp1 = (int **)malloc((len1+1)*sizeof(int *));
	for(int i=0;i<=len1;++i){
		tmp1[i] = (int *)malloc((len2+1)*sizeof(int));
	}
	tmp2 = (int **)malloc((len1+1)*sizeof(int *));
	for(int i=0;i<=len1;++i){
		tmp2[i] = (int *)malloc((len2+1)*sizeof(int));
	}
	for(int i=0;i<=len1;++i){//标明边界
		tmp1[i][0] = 0;
		tmp2[i][0] = 0;
	}
	for(int i=0;i<=len2;++i){//0表示边界  不指向任何方向 1向左,2向右,3向上
		tmp1[0][i] = 0;
		tmp2[0][i] = 0;
	}
	for(int i=1;i<=len1;++i)
		for(int j=1;j<=len2;++j){
			if(sq1.at(i-1)==sq2.at(j-1)){
				tmp1[i][j] = tmp1[i-1][j-1] +1;
				tmp2[i][j] = 2;
			}
			else {
				if(tmp1[i-1][j]>tmp1[i][j-1]){
					tmp1[i][j] = tmp1[i-1][j];
					tmp2[i][j] = 3;//指向上
				}
				else{
					tmp1[i][j] = tmp1[i][j-1];
					tmp2[i][j] = 1;//指向左
				}
			}
		}
}
void outpuLCS(string sq1,int **tmp,int len1,int len2){	
	if(len1==0||len2==0)return;
	if(tmp[len1][len2]==2){	
		//cout<<sq1.at(len1-1);
		squence.push(sq1.at(len1-1));
		outpuLCS(sq1,tmp,len1-1, len2-1);		
	}
	else if(tmp[len1][len2]==1){
		outpuLCS(sq1,tmp,len1, len2-1);
	}
	else {
		outpuLCS(sq1,tmp,len1-1, len2);
	}
}
int main(){
	int **len,**path;
	string str1,str2;
	str1 = "abcdefg";
	str2 = "bcgefd";
	len = NULL;path = NULL;
	getLCS(len,path,str1.size(),str2.size(),str1,str2);
	outpuLCS(str1,path,str1.size(),str2.size());
	while(!squence.empty()){
		cout<<squence.top();
		squence.pop();
	}
	cout<<endl;
	free(len);free(path);
}
第四步根据方向信息构造最优解。

需要说明的是有时候最优解并不唯一,但是在这里并没有说明,感兴趣的朋友可以研究下。

有时间了我会回过头来贴出求解所有最优解的代码。

下面我们接着分析其时间复杂度:

将getLCS函数中的比较最后一个双层循环的比较语句视为关键语句,得到时间复杂度为:


所以动态规划的时间复杂度为O(mn)。

有木有感觉有点意思?下面再来一个最短公共超序列的求解代码,相信有了前面的基础这里就不需要再进行详细的说明。

这里说明一下最短公共超序列,也就是同时包含所有子序列信息的一个最短的序列,可以理解为子序列的不重读融合。

代码如下:

#include<iostream>
#include<string>
#include<stack>
using namespace std;
stack<char> squence;
void getSCS(int **&tmp1,int **&tmp2,int len1,int len2,string sq1,string sq2){
	//两个矩阵  一个存长度  一个存方向
	tmp1 = (int **)malloc((len1+1)*sizeof(int *));
	for(int i=0;i<=len1;++i){
		tmp1[i] = (int *)malloc((len2+1)*sizeof(int));
	}
	tmp2 = (int **)malloc((len1+1)*sizeof(int *));
	for(int i=0;i<=len1;++i){
		tmp2[i] = (int *)malloc((len2+1)*sizeof(int));
	}
	for(int i=0;i<=len1;++i){//标明边界
		tmp1[i][0] = i;
		tmp2[i][0] = 0;
	}
	for(int i=0;i<=len2;++i){//0表示边界  不指向任何方向 1向左,2向右,3向上
		tmp1[0][i] = i;
		tmp2[0][i] = 0;
	}
	for(int i=1;i<=len1;++i)
		for(int j=1;j<=len2;++j){
			if(sq1.at(i-1)==sq2.at(j-1)){
				tmp1[i][j] = tmp1[i-1][j-1] +1;
				tmp2[i][j] = 2;
			}
			else {
				if(tmp1[i-1][j]>tmp1[i][j-1]){
					tmp1[i][j] = tmp1[i][j-1]+1;
					tmp2[i][j] = 1;//指向左
				}
				else{
					tmp1[i][j] = tmp1[i-1][j]+1;
					tmp2[i][j] = 3;//指向上
				}
			}
		}
}
void outpuSCS(string sq1,string sq2,int **tmp,int len1,int len2){	
	if(len1==0){
		while(len2--)squence.push(sq2.at(len2));
		return;
	}
	if(len2==0){
		while(len1--)squence.push(sq1.at(len1));
		return;
	}
	if(tmp[len1][len2]==2){	
		//cout<<sq1.at(len1-1);
		squence.push(sq1.at(len1-1));
		outpuSCS(sq1,sq2,tmp,len1-1, len2-1);		
	}
	else if(tmp[len1][len2]==1){
		squence.push(sq2.at(len2-1));
		outpuSCS(sq1,sq2,tmp,len1, len2-1);
	}
	else {
		squence.push(sq1.at(len1-1));
		outpuSCS(sq1,sq2,tmp,len1-1, len2);
	}
}
int main(){
	int **len,**path;
	string str1,str2;
	str1 = "ABCBDAB";
	str2 = "BDCABA";
	len = NULL;path = NULL;
	getSCS(len,path,str1.size(),str2.size(),str1,str2);
	outpuSCS(str1,str2,path,str1.size(),str2.size());
	while(!squence.empty()){
		cout<<squence.top();
		squence.pop();
	}
	cout<<endl;
	for(int i=0;i<=str1.size();++i){
		for(int j=0;j<=str2.size();++j)
			cout<<len[i][j]<<" ";
		cout<<endl;
	}
	cout<<endl;
	for(int i=0;i<=str1.size();++i){
		for(int j=0;j<=str2.size();++j)
			cout<<path[i][j]<<" ";
		cout<<endl;
	}
	free(len);free(path);
}

若文章有什么问题,欢迎大家指正,共同学习共同进步。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值