持续更新...
15.1:来一个例子:装配线调度,主要是找到最有子结构,注意的是找到之后的解决方法,不能够通过给出的式子直接的进行操作,那样的话会造成求解的过程时间复杂度很高,但是使用顺序求解就是O(n)完成,代码如下;
/**动态规划之装配线调度**/
#include <iostream>
using namespace std;
#define N 6
class cost
{
public:
int f1[N+1];
int f2[N+1];
int e1;//进入线路1的时间
int e2;//进入线路2的时间
int x1;//离开线路1时间
int x2;//离开线路2时间
int a1[N+1];//从线路1离开所消耗的时间
int a2[N+1];//从线路2离开所消耗的时间
int t1[N+1];
int t2[N+1];
int l1[N+1];
int l2[N+1];
int fcost;
int l_last;
cost()
{
cout<<"初始化......"<<endl;
for (int i=1;i<=N;++i)
{
f1[i]=0;
f2[i]=0;
a1[i]=0;
a2[i]=0;
t1[i]=0;
t2[i]=0;
l1[i]=0;
l2[i]=0;
}
fcost=0;
l_last=0;
}
void cost_init()
{
cout<<"输入进入线路1,线路2的时间..."<<endl;
cin>>e1>>e2;
cout<<"输入离开线路1,线路2的时间..."<<endl;
cin>>x1>>x2;
cout<<"输入各个装配站的消耗时间..."<<endl;
for (int j=1;j<=N;++j)
{
cin>>a1[j]>>a2[j];
}
cout<<"输入装配站移走消耗时间..."<<endl;
for (int k=1;k<=N-1;++k)
{
cin>>t1[k]>>t2[k];
}
}
void fastest_way()
{
f1[1]=e1+a1[1];
f2[1]=e2+a2[1];
for(int num=2;num<=N;++num)
{
if(f1[num-1]+a1[num]<=f2[num-1]+a1[num]+t2[num-1])
{
f1[num]=f1[num-1]+a1[num];
l1[num]=1;
}else
{
f1[num]=f2[num-1]+a1[num]+t2[num-1];
l1[num]=2;
}
if(f2[num-1]+a2[num]<=f1[num-1]+a2[num]+t1[num-1])
{
f2[num]=f2[num-1]+a2[num];
l2[num]=2;
}else
{
f2[num]=f1[num-1]+a2[num]+t1[num-1];
l2[num]=1;
}
}
if(f1[N]+x1<=f2[N]+x2)
{
fcost=f1[N]+x1;
l_last=1;
}else
{
fcost=f2[N]+x2;
l_last=2;
}
}
void print_station()
{
int i=l_last;
cout<<"line "<<i<<"station "<<N<<endl;
for (int k=N;k>=2;--k)
{
if(i==1)
{
i=l1[k];
cout<<"line "<<i<<"station "<<k-1<<endl;
}else if(i==2)
{
i=l2[k];
cout<<"line "<<i<<"station "<<k-1<<endl;
}
}
for (int q=1;q<=N;++q)
{
cout<<f1[q]<<" ";
}
cout<<endl;
for (int m=1;m<=N;++m)
{
cout<<f2[m]<<" ";
}
cout<<endl;
cout<<"l_last :" << l_last<<endl;
for (int s=2;s<=N;++s)
{
cout<<l1[s]<<" ";
}
cout<<endl;
for (int t=2;t<=N;++t)
{
cout<<l2[t]<<" ";
}
cout<<endl;
}
void former_print_station(int j,int n)
{
int i=l_last;
if (j==0)
return ;
else if(j>1)
{
former_print_station(j-1,n);
}
if (j==n)
{
i=l_last;
cout<<"station :"<<j<<"line :"<<i<<endl;
}
else
{
if(i==1)
{
i=l1[j+1];
cout<<"station :"<<j<<"line :"<<i<<endl;
}
else if(i==2)
{
i=l2[j+1];
cout<<"station :"<<j<<"line :"<<i<<endl;
}
}
}//15.1-1
};
int main()
{
cost cst;
cst.cost_init();
cst.fastest_way();
cst.print_station();
cout<<"--------------------------"<<endl;
cst.former_print_station(N,N);
return 0;
}
/*******************************分割线********************************************************/
#include <iostream>
#include <vector>
using namespace std;
#define N 6//这里是如此的设计
#define ROW 6
#define COLUMN 6
int m[N+1][N+1];
int s[N+1][N+1];
struct matrix_simple//不计算,仅算下次数
{
int row;
int column;
matrix_simple(int r,int c):row(r),column(c){}
};
struct matrix
{
int row;
int column;
int num[ROW][COLUMN];
matrix()
{
for (int i=0;i<ROW;++i)
for (int j=0;j<COLUMN;++j)
{
num[i][j]=0;
}
}
matrix(int r,int c):row(r),column(c){
for (int i=0;i<row;++i)
for (int j=0;j<column;++j)
{
cin>>num[i][j];
}//对每一个进行初始化
}
};
void init(int (*q)[N+1],int (*p)[N+1])
{
for (int i=0;i<=N;++i)
for (int j=0;j<=N;++j)
{
q[i][j]=0;
p[i][j]=0;
}
}
void mark_chain_order(vector<matrix_simple> vec,int (*q)[N+1],int (*p)[N+1])
{
int n=vec.size();
for (int s=1;s<=n;++s)
{
q[s][s]=0;//单个矩阵做乘法的次数是0
}
for (int l=2;l<=n;++l)
{
for (int i=1;i<=n-l+1;++i)
{
int j=i+l-1;
q[i][j]=0xfffffff;
int k;
for (k=i;k<j;++k)
{
int qum=q[i][k]+m[k+1][j]+vec[i].row*vec[k].row*vec[j].column;
if(qum<q[i][j])
{
q[i][j]=qum;
p[i][j]=k;
}
}
}
}
}
void print_optimal_parens(int (*s)[N+1],int i,int j)
{
if(i==j)
cout<<"A["<<i<<"]";
else
{
cout<<"(";
print_optimal_parens(s,i,s[i][j]);
print_optimal_parens(s,s[i][j]+1,j);
cout<<")";
}
}
void print_num(int (*m)[N+1])
{
for (int i=1;i<=N;++i)
for (int j=1;j<=N;++j)
{
cout<<m[i][j]<<" ";
}
cout<<endl;
}
//15.2-2
matrix matrix_multiply(matrix a,matrix b)
{
matrix c(a.row,b.column);
if(a.column!=b.row)
{
cerr<<"error"<<endl;
exit(127);
}else
{
for (int i=0;i<a.row;++i)
for(int j=0;j<b.column;++j)
for(int k=0;k<a.column;++k)
{
c.num[i][j]=a.num[i][k]*b.num[k][j];
}
}
return c;
}
matrix matrix_chian_multiply(vector<matrix> vec,int (*s)[N+1],int i,int j)
{
matrix b1,b2;
if (i==j)
return vec[i];
else if(j=i+1)
{
return matrix_multiply(vec[i],vec[j]);
}else
{
b1=matrix_chian_multiply(vec,s,i,s[i][j]);
b2=matrix_chian_multiply(vec,s,s[i][j]+1,j);
return matrix_multiply(b1,b2);
}
};
int recursive_matrix_chain(vector<matrix> vec,int i,int j)
{
int q;
if(i==j)
return 0;
m[i][j]=0xfffffff;
for (int k=1;k<j;++k)
{
q=recursive_matrix_chain(vec,i,m[i][j])+recursive_matrix_chain(vec,m[i][j]+1,j)+vec[i].row*vec[k].column*vec[j].column;
if(q<m[i][j])
m[i][j]=q;
}
return m[i][j];
}/*
算法解析:这个是计算出m[i][j]的一个递归算法,但是时间复杂度是很高的,由于是这个算法是从上向下的计算出各个m[i][j],可能的情况是各阶段的m[i][j]
的计算具有重复的可能性,由于重复的计算了很多的相同的阶段,那么必定耗时,根据这个算法的数学式子进行获取具体的计算过程可以参看算法导论p206得打算法的
时间复杂度是指数级别的,由于重算了很多相同部分,这是自上向下算法的一个不好之处,对于之前的那些算法是不会存在这种问题的,当然也存在着自上向下的算法
其时间复杂度和之前提的自底向上的时间复杂度一样O(n^3),那就是下面的备忘问题,她采取的方法是记录下那些曾经计算的东西,一开始的m[i][j]都是赋值为很大,一旦计算
出一个新值就替换极大值,之后的话如果再次的遇到这种非极大值,不用计算,直接返回。
*/
int look_up(vector<matrix> vec,int i,int j)
{
int q;
if (m[i][j]<0)
return m[i][j];
if(i==j)
m[i][j]=0;
else
{
for(int k=i;k<j;++k)
{
q=look_up(vec,i,m[i][j])+look_up(vec,m[i][j]+1,j)+vec[i].row*vec[k].column*vec[j].column;
if(q<m[i][j])
m[i][j]=q;
}
}
return m[i][j];//这个的话是把m[i][j]最初是赋值是很大的值,当遇到值是小于这个的时候就直接进行一系列的其他的操作,即一遇到已经计算的
//非极大的值就直接使用,不用再次的计算,因为以计算的话设计到子问题重叠,那就比较复杂了。
}
int memory_matrix_chain(vector<matrix> vec)
{
int n=vec.size();
for (int i=1;i<=n;++i)
for (int j=i;j<=n;++j)
m[i][j]=0xfffffff;
return look_up(vec,1,n);
}
int main()
{
/* vector<matrix_simple> vec;
vec.push_back(matrix_simple(5,10));//插入矩阵:30*35
vec.push_back(matrix_simple(10,3));
vec.push_back(matrix_simple(3,12));
vec.push_back(matrix_simple(12,5));
vec.push_back(matrix_simple(5,50));
vec.push_back(matrix_simple(50,6));
init(m,s);
mark_chain_order(vec,m,s);
print_optimal_parens(s,1,6);
cout<<endl;
print_num(m);
cout<<endl;*/
cout<<"-------------"<<endl;
vector<matrix> vecs;
matrix a(2,3);
matrix b(3,1);
vecs.push_back(a);
vecs.push_back(b);
cout<<a.column<<" "<<b.row<<endl;
matrix_chian_multiply(vecs,s,0,vecs.size()-1);
cout<<"-------------"<<endl;
return 0;
}
#include <iostream>
#include <string>
using namespace std;
/*
概念:
子序列:存在序列X={x1,x2,...,xm},序列Z={z1,z2,...,zk},序列Z如果要是序列X的子序列的话,满足的条件是:对于序列X中某一位开始(该位和Z中第一个z1相同),依次往后的话,X中从该位开始往后存在元素次序
是递增,但不一定要连续,这些递增的元素依次和Z中z2,z3,,...,zk相同,那么的话Z就就是X的子序列;
公共子序列:序列X,序列Y,以及序列Z,如果Z是X的子序列,也是Y的子序列,那么Z就是X和Y的公共子序列。
最长公共子序列:就是对于序列X,和序列Y而言的话,存在的公共子序列的长度是最长的。
***********求最长公共子序列的方法*****************
(1)暴力方法:序列X中依次探测子序列,有2^(x.length)种,各个子序列分别看是不是,序列Y的子序列即可。
(2) 动态规划:
序列的第i个前缀:序列X={x1,x2,...,xm}的第i个前缀是{x1,x2,...,xi};
定理:
最优子结构:对于序列X={x1,x2,...,xm}和序列Y={y1,y2,...,yn},如果存在序列Z={z1,z2,...,zk}是属于X,Y的一个最长公共子序列的话,那么存在着下面的结论:
(1)if xm== yn ;then zk=xm=yn ,Z(k-1)是 X(m-1)和Y(n-1)的最长公共子序列;
(2)if xm!=yn ;if xm!=zk 意味着Z是X(m-1)和Y的最长公共子序列;
(3)if xm!=yn ;if yn!=zk 意味着Z是Y(n-1)和X的最长公共子序列。
*/
#define N 10
int c[N+1][N+1];
char b[N+1][N+1];
struct CS
{
string str;
CS(){
cin>>str;
}
};
void init(int (*a)[N+1],char (*b)[N+1])
{
for (int i=0;i<=N;++i)
for (int j=0;j<=N;++j)
{
a[i][j]=0;
b[i][j]=' ';
}
}
void lcs_length(CS cs1,CS cs2)
{
int length1=cs1.str.length();
int length2=cs2.str.length();
for (int i=1;i<=length1;++i)
c[i][0]=0;
for (int j=1;j<=length2;++j)
c[0][j]=0;
for (int s=1;s<=length1;++s)
for (int t=1;t<=length2;++t)
{
if (cs1.str[s]==cs2.str[t])
{
c[s][t]=c[s-1][t-1]+1;
b[s][t]='m';
}
else
if(c[s-1][t]>=c[s][t-1])
{
c[s][t]=c[s-1][t];
b[s][t]='u';
}else
{
c[s][t]=c[s][t-1];
b[s][t]='l';
}
}
}
void print_lcs(char (*b)[N+1],CS cs1,int i,int j)
{
if (i==0||j==0)
return;
if (b[i][j]=='m')
{
print_lcs(b,cs1,i-1,j-1);
cout<<cs1.str[i];
}
else
if (b[i][j]=='u')
print_lcs(b,cs1,i-1,j);
else if (b[i][j]=='l')
print_lcs(b,cs1,i,j-1);
}
//15.4-2
void print_lcs1(int (*c)[N+1],CS cs1,CS cs2,int i,int j)
{
if (i==0||j==0)
return ;
if (cs1.str[i]==cs2.str[j])
{
print_lcs1(c,cs1,cs2,i-1,j-1);
cout<<cs1.str[i];
}else
{
if (c[i-1][j]>c[i][j-1])
{
print_lcs1(c,cs1,cs2,i-1,j);
}else
{
print_lcs1(c,cs1,cs2,i,j-1);
}
}
}
//15.4-3
int main()
{
CS cs1;//为了和书上的例子下标同步,输入的时候字符串的第一个都输入0做哨兵
CS cs2;
init(c,b);
lcs_length(cs1,cs2);
// print_lcs(b,cs1,cs1.str.size(),cs2.str.size());
print_lcs1(c,cs1,cs2,cs1.str.size(),cs2.str.size());
return 0;
}
/*
思路总结:对于这种情况的话:一般求一个最长子序列的话,采用记录方法计算出最长的子序列的长度,之后由于有一个最优子结构的方法可以得到
子序列的关系,可以把这种子序列的长度进行一定程度的表示,再就是最后的输出,由于我们再对于每一种情况下的序列元素相等的时候都可以进行一定标记
通过这种标记就可以输出,可以在子序列之和的时间复杂度范围内求出来。所以一般情况下求解的时侯不一定需要安装字面意思去求解序列,可以先从
序列长度来进行,这里考虑到的动态规划的分析就是:自由子结构,这里的话就是最有子结构的这个情况给了最好的解释,之后的话就是求解了,自底向上的话
从标记的每一步开始进行使用递归的方法,但是递归次数是可以知道的。
*/
//课后习题 15.4-1:把序列输入就可以知道了记住是第一个哨兵,我们使用很大的数字开始得到的结果是:100110
//15.4-2 由于使用b是用空间换时间的一种方法,这里不使用b还可以时间复杂度还是不错的方法:方法见上面
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
/*
最优二叉查找树:
简介:既然是最优就需要有一个衡量的标准,比如按照书上的说,使用二叉查找树作为查找的一个工具,键值作为需要查找的容器,相应的查找每一个键值会需
一个对应的查找概率,因为可能有的键值查找的频繁就会跟根近一点,等,书上给出了一个计算的公式就是对应的二叉树的查找期望,具体描述如下:键值序列:
<k1,k2,k3,...,kn>对应的查找概率为<p1,p2,p3,..,pn>,此外对于键值之间的书本还给了一个虚键<d0,d1,d2,...,dn>,具体的虚键是如何摆放的可以参见书本,
对应的概率可以描述为<p0,p1,p2,...,pn>,对于某一个键值ki(高度是hi,概率pi)如果被查找到的的期望(单键值期望):(hi+1)*pi,那么的话对于整棵二叉树
可以计算出最终的期望了;书上就是以整棵树的期望为基准作为左右的查找二叉树的标准。
采用动态规划方法解决:得到最优子结构:我们假设对于序列<ki,ki+1,...,kj>对应虚键<di-1,di,...,dj>,这样一个二叉树的搜索期望是e[i][j],我们假设从kr
键开始令该点为根的话得到的是一个最优的二叉查找树,那么的话就是<ki,ki+1,...,kr-1>为左子树,kr为根,<kr+1,kr+2,...,kj>为右子树,对应的需要再以
左右子树为对像如此计算最优二叉查找树,这就是最优子结构。上面需要考虑的还有一种特殊情况j=i-1,这个时候的话就令只有一个虚键,对应期望就是p[i][i-1]=qi;
如此可以得到的结论如下:
(1)当j=i-1的时候:e[i][j]=qi*(1+0);
(2)当j>=i的时候:e[i][j]=pr*(1+0)+e[i][r-1]+w[i][r-1]+e[r][j]+w[r][j];//w[i][j]的表达式不能够在这个环境表示,具体见书本;
对于每一个r的话我们会使用root[i][j]进行记录;此外即使我们有了上面的一个递归的式子,但是为了防止直接使用递归的时候造成最后的结果是指数级别的
那么我们采用自底向上的方法可以避免子问题重复计算的情况。具体见下面代码;
*/
#define N 10
struct tree
{
int start;
int end;
int root[N+1][N+1];
float w[N+1][N+1];
float e[N+1][N+1];
float p[N+1];
float q[N+1];
// char key[N+1];
tree(int s,int ed):start(s),end(ed){
for (int i=0;i<N+1;++i)
{
for (int j=0;j<N+1;++j)
{
root[i][j]=0;
w[i][j]=0;
e[i][j]=0;
}
p[i]=0;
q[i]=0;
// key[i]=' ';
}
}
void init();
void optimal_bst();
void construct_optimal_bst(int,int);
};
void tree::init()
{
cout<<"p key input ..."<<endl;
for (int k=start;k<=end;++k)
{
float tmp1;
cin>>tmp1;
p[k]=tmp1;
}
cout<<"q key input ..."<<endl;
for (int j=start-1;j<=end;++j)
{
float tmp2;
cin>>tmp2;
q[j]=tmp2;
}
// for (int i=start;i<=end;++i)
// {
// cin>>key[i];
// }
}
void tree::optimal_bst()
{
for (int i=start;i<=end+1;++i)
{
e[i][i-1]=q[i-1];
w[i][i-1]=q[i-1];
}
for (int l=start;l<=end;++l)
{
for (int i=start;i<=end-l+1;++i)
{
int j=i+l-1;
e[i][j]=0xfffffff;
w[i][j]=w[i][j-1]+p[j]+q[j];
for (int r=i;r<=j;++r)
{
float t=e[i][r-1]+e[r+1][j]+w[i][j];
if (t<e[i][j])
{
e[i][j]=t;
root[i][j]=r;
}
}
}
}
}
void tree::construct_optimal_bst(int i,int j)//15.5-1
{
if (i==start&&j==end)
cout<<"k"<<root[i][j]<<" is the root "<<endl;
if (i==j)
// cout<<key[i]<<" ";
{
cout<<"d"<<i-1<<" is the left child of k"<<i<<endl;
cout<<"d"<<i<<" is the right child of k"<<i<<endl;
}
if (i>j)
{
cout<<"d"<<j<<" is the right child of k"<<j<<endl;
}
else if(i<j)
{
int r=root[i][j];
// cout<<key[r]<<" ";
if (root[i][r-1]==0)
{
//cout<<"d"<<r<<" is the left child of k"<<r<<endl;
}
else
cout<<"k"<<root[i][r-1]<<" is the left child of k"<<r<<endl;
construct_optimal_bst(i,r-1);
if (root[r+1][j]==0)
{
//cout<<"d"<<r+1<<" is the right child of k"<<r<<endl;
}
else
cout<<"k"<<root[r+1][j]<<" is the right child of k"<<r<<endl;
construct_optimal_bst(r+1,j);
}
}
//15.5-1
/*
*/
int main()
{
/* tree tre(1,5);
tre.init();
tre.optimal_bst();
tre.construct_optimal_bst(1,5);
*/
//15.5-1
tree tre(1,7);
tre.init();
tre.optimal_bst();
tre.construct_optimal_bst(1,7);
return 0;
}