动态规划(算法分析与设计)

来源:互联网 发布:安装ubuntu出错404 编辑:程序博客网 时间:2024/05/04 14:54

0.简单动态规划做法相关笔记

A.动态规划算法设计

1.找出最优解性质,刻画最优解结构(有什么窍门?周五上课问老师)

我们把这种子问题最优时,母问题通过优化选择后一定最优的情况叫做“最优子结构”。

2.递归的定义最优值(子问题重叠)

自顶而下定义最优值(备忘录法)

3.以自底向上的方式计算最优值

自底而上的迭代

4.通过子问题的最优值构造原问题的最优解

母问题通过对子问题最优值的优化选择,得出最优解

B.动态规划中一些代码的模板(矩阵连乘,图像压缩,0-1背包差不多就这三种模板套)

1.起点,终点都变动m[ i ][ j ](O(n^3))

int Count(int n){for(int i=0;i<=n;i++){m[i][i]=v[i];} for(int r=2;r<=n+1;r++)//本次划分成的小组,每小组元素的个数 {for(int i=0;i<=n-r+1;i++)//m[i][j],左下标i的上界为:(最右下标-小组元素个数+1) {int j=i+r-1;//m[i][j]右下标的标号为:(左下标序i+小组元素个数-1)m[i][j]=GetV(m[i][i],m[i+1][j],i,i+1);for(int k=i+1;k<j;k++)//组内划分,从最下标+1开始,到右下标结束 {int temp=GetV(m[i][k],m[k+1][j],k,k+1);if(temp>m[i][j]){m[i][j]=temp;}}}}return m[0][n];}

2.起点终点只有一个变动(O(n^2))

int CountMin(vector<P> &p,vector<int> &s,vector<int> &l){int N=p.size()-1;//元素最大下标 for(int i=1;i<=N;i++)//从1开始遍历,p[0]无效,s[0]无效 {int bmax=p[i].bitp;s[i]=s[i-1]+bmax;//s[i]初始化为s[i-1]已经得出的i-1的最佳划分与最后一个元素自成一段 l[i]=1;//从下标i起,该段长度 for(int j=2;j<=i&&j<=lmax;j++)//j为该段元素可能的个数,从2个到256限制,或到目标位置i为止 {if(bmax<p[i-j+1].bitp)//遍历是从靠近i的最左边元素i-1开始的 {bmax=p[i-j+1].bitp;}if(s[i]>s[i-j]+j*bmax)//该段向左扩张,依次比较扩张一个单元,与不扩张该单元的大小 {s[i]=s[i-j]+j*bmax;l[i]=j;}}s[i]+=header;}return s[N];}

C.动态规划与分治的一点区别

1.动态规划适用问题类型:(组合最优化和最优化问题)优化问题。分治适用问题类型:通用问题

3.动态规划的实现方法一般是自底向上,分治是自顶向下

4.分治法将子问题看成独立的(如果实际上子问题不独立,那就要重复求解公共子问题),动态规划将子问题看成联系的(各子问题包含公共子子问题,对每个子子问题只求解一次)。

5.动态规划的子问题规模通常只比原问题规模小1,分治的子问题之间的规模通常差不多,比原问题规模要小上一大截

1.矩阵连乘问题

参考博客:http://blog.sina.com.cn/s/blog_64018c250100s123.html

备忘录方法求解与动态规划求解之间的关系,有些像《分治递归》中非尾递归转非递归的关系。

A.动态规划---自底而上的迭代

a.i*k型号的矩阵与k*j型号的矩阵相乘,乘法次数为i*k*j,即行i*列k*列j,即列(i-1)*列k*列j

b.假如任意一个规模小于j-i+1的矩阵最少乘法次数m[ i ][ k ]已知,那么j-i+1规模的矩阵连乘最少次数也应易知m[ i ][ j ]=min{ m[ i ][ k ]+m[ k ][ j ]+i*k*j },故所求者只是划分位置k

#include<iostream>#include<vector>#include<cmath>using namespace std;void matrixChain(vector<int> &p,vector< vector<int> > &m,vector< vector<int> > &s)//p[]保存的是矩阵i~j的列数;m[a][b]用于保存b-a+1规模的矩阵连乘的最少乘法次数;s[a][b]保存b-a+1规模的矩阵连乘的最佳分割点k {int n=p.size()-1;//矩阵连乘最大序号for(int i=1;i<=n;i++){m[i][i]=0;//矩阵连乘的矩阵下标是从i=1开始的,单个矩阵时,乘法次数为0 }for(int r=2;r<=n;r++)//r为每组元素个数,每组元素个数的递增使得循环由计算m[i][i+1](i=1~n)到计算m[i][j](n>j>i+1) {for(int i=1;i<=n-r+1;i++)//从m[2][3],m[3][4],m[5][6]算起,随着每组元素个数的增多开始算m[1][10]这样的数,//这样可以保证沿途记录了较大m[i][j]计算所需的m[i][k]和m[k][j]的确切值(i<k<j),就实现了自底向上的计算过程。 {int j=i+r-1;//m[i][j]即为m[i][i+(r-1)],i循环,r-1递增 m[i][j]=m[i][i]+m[i+1][j]+p[i-1]*p[i]*p[j];//m[i][j]初始化为(Ai * (Ai+1~Aj最小乘法次数) ),因m[i][i]=0,所以其实可以省略掉m[i][i] s[i][j]=i;//当前j+i-1规模矩阵连乘最佳分割处为k=ifor(int k=i+1;k<j;k++)//遍历(i+1)~(j-1)寻找最小乘法次数及最佳分割点,上界为j是因为m[][]的确切值,只算到了m[][j];下界自然是i+1了 {  //由于j=i+r-1,且i是递增的,所以整一个m[][]的记录是自底向上的, int t=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];//i*j型矩阵与j*k型矩阵相乘乘法次数为i*j*k,除p[0]存储的是第1个矩阵的行数,p[i]存储的是第i个矩阵的列数 if(t<m[i][j])//矩阵i的行等于矩阵i-1的列,所以行i*列k*列j,等于列i-1*列k*列j,即p[i-1]*p[k]*p[j] {m[i][j]=t;s[i][j]=k;}} }}}int randrom(int start,int end){return start+rand()%(end-start+1);} void traceback(vector< vector<int> > &s,int i,int j){if(i==j){return;}traceback(s,i,s[i][j]);traceback(s,s[i][j]+1,j);cout<<"A"<<i<<","<<s[i][j]<<"and A"<<s[i][j]+1<<","<<j<<endl;}int main(){vector<int> p;vector< vector<int> > m;vector< vector<int> > s;cout<<"please input the number of matrix!"<<endl;int n;cin>>n;for(int i=0;i<=n;i++){p.push_back(randrom(1,20));vector<int> temp;for(int j=0;j<=n;j++){temp.push_back(-1);}m.push_back(temp);s.push_back(temp);}matrixChain(p,m,s);cout<<"please input i and j!"<<endl;int i,j;cin>>i>>j;traceback(s,i,j);} 

B.备忘录---自顶而下的递归

#include<iostream>#include<vector>#include<cmath>using namespace std;int matrixChain(vector<int> &p,vector< vector<int> > &m,vector< vector<int> > &s,int i,int j){if(m[i][j]>0)//若已经记录,就直接返回记录值 {return m[i][j];//m为备忘录,记载已经得到确切值的m[i][j] }else if(i==j)//单个矩阵,乘法次数为0,直接返回0 {return 0;}//其余,递归求解 else{m[i][j]=m[i][i]+matrixChain(p,m,s,i+1,j)+p[i-1]*p[i]*p[j];//初始化为Ai*(Ai+1~Aj)s[i][j]=i; for(int k=i+1;k<j;k++)//求m[i][j]的确切值 {int t=matrixChain(p,m,s,i,k)+matrixChain(p,m,s,k+1,j)+p[i-1]*p[k]*p[j];if(t<m[i][j]){m[i][j]=t;s[i][j]=k;}}return m[i][j];}} int randrom(int start,int end){return start+rand()%(end-start+1);} void traceback(vector< vector<int> > &s,int i,int j){if(i==j){return;}traceback(s,i,s[i][j]);traceback(s,s[i][j]+1,j);cout<<"A"<<i<<","<<s[i][j]<<"and A"<<s[i][j]+1<<","<<j<<endl;}int main(){vector<int> p;vector< vector<int> > m;vector< vector<int> > s;cout<<"please input the number of matrix!"<<endl;int n;cin>>n;for(int i=0;i<=n;i++){p.push_back(randrom(1,20));vector<int> temp;for(int j=0;j<=n;j++){temp.push_back(0);}m.push_back(temp);s.push_back(temp);}matrixChain(p,m,s,1,n);cout<<"please input i and j!"<<endl;int i,j;cin>>i>>j;traceback(s,i,j);} 

2.最长公共子序列

A.备忘录

#include<iostream> #include<vector>#include<cmath>#include<cstdlib>using namespace std;int random(int start,int end){return start+rand()%(end-start+1);}//和矩阵连乘的思想一样,如果我们能知道链长为ix和链长为jy的最长公共子序列的确切值m[ix][jy](ix<i;iy<y),//那么加上一个元素,我们就能算出 m[ix+1][jy]的最长公共子序列的确切值,依次增加ix,jy,可推出我们最终可以确切知道m[i][j] //这个子集从矩阵连乘和最长公共子序列来看,并不是像分治那般,切割成差不多规模的子集。而是这个子集+1就可以成为父集,是属于一个元素一个元素剥下来的 //m[i][j]记录长i的链x,与长j的链y的公共子序列,和矩阵连乘一样有一个二维数组来记录原问题(这里记录最长公共子序列的长度,矩阵连乘记录最少乘法次数)//且都是从原子问题逐步扩大 int lcslength(vector<int> &x,vector<int> &y,vector< vector<int> > &m,int i,int j){if(m[i][j]>0){return m[i][j];}if(i==0||j==0){return 0;}if(x[i]==y[j]){m[i][j]=lcslength(x,y,m,i-1,j-1)+1;//最后一个元素相等,m[i][j]=m[i-1][j-1]+1 return m[i][j];}m[i][j]=max(lcslength(x,y,m,i,j-1),lcslength(x,y,m,i-1,j));//最后一个元素不相等,那就是max{m[i][j-1],m[i-1][j]},即两个链表中的一个链表要去掉一个元素 return m[i][j];}int main(){vector<int> x;vector<int> y;int xlength;int ylength;cin>>xlength>>ylength;cout<<"x is:"<<endl;x.push_back(-1);//从下标1开始存储元素,递归中当i=0或j=0时返回,若在下标0处开始存储,首位就检测不到了 for(int i=1;i<xlength;i++){x.push_back(random(1,3));cout<<x[i]<<"\t";}cout<<"\ny is:"<<endl;y.push_back(-1);//从下标1开始存储元素 for(int i=1;i<ylength;i++){y.push_back(random(1,3));cout<<y[i]<<"\t";}cout<<endl;vector< vector<int> > m;for(int i=0;i<xlength;i++){vector<int> temp;for(int j=0;j<ylength;j++){temp.push_back(0);}m.push_back(temp);}cout<<lcslength(x,y,m,xlength-1,ylength-1)<<endl;;} 

B.动态规划

#include<iostream> #include<vector>#include<cmath>#include<cstdlib>#include<stack>using namespace std;int random(int start,int end){return start+rand()%(end-start+1);}void lcslength(vector<int> &x,vector<int> &y,vector< vector<int> > &m,int xi,int yj,vector< vector<int> >&b){if(xi==0||yj==0){return;}for(int i=1;i<=xi;i++){for(int j=1;j<=yj;j++){if(x[i]==y[j]){m[i][j]=m[i-1][j-1]+1;b[i][j]=1;}else{m[i][j]=max(m[i][j-1],m[i-1][j]);if(m[i][j-1]>m[i-1][j]){b[i][j]=2;}else{b[i][j]=3;}}}}}stack<int> s;void showlcs(vector< vector<int> > &b,int i,int j,vector<int> &x,vector<int> &y){if(i==0||j==0){while(!s.empty()){cout<<s.top()<<" ";s.pop();}return;}if(b[i][j]==1){s.push(x[i]);showlcs(b,i-1,j-1,x,y);}else if(b[i][j]==2){showlcs(b,i,j-1,x,y);}else{showlcs(b,i-1,j,x,y);}}int main(){vector<int> x;vector<int> y;int xlength;int ylength;cout<<"please input xlength and y length!"<<endl;cin>>xlength>>ylength;cout<<"x is:"<<endl;x.push_back(-1);//从下标1开始存储元素,递归中当i=0或j=0时返回,若在下标0处开始存储,首位就检测不到了 for(int i=1;i<xlength;i++){x.push_back(random(1,3));cout<<x[i]<<"\t";}cout<<"\ny is:"<<endl;y.push_back(-1);//从下标1开始存储元素 for(int i=1;i<ylength;i++){y.push_back(random(1,3));cout<<y[i]<<"\t";}cout<<endl;vector< vector<int> > m;vector< vector<int> > b;for(int i=0;i<xlength;i++){vector<int> temp;for(int j=0;j<ylength;j++){temp.push_back(0);}m.push_back(temp);b.push_back(temp);}lcslength(x,y,m,xlength-1,ylength-1,b);cout<<"\nm["<<xlength-1<<"]["<<ylength-1<<"]: "<<m[xlength-1][ylength-1]<<endl;showlcs(b,xlength-1,ylength-1,x,y);} 

3.多边形游戏(O(n^4))

书上做法的时间复杂度是O(n^3)。我的做法是遍历的删除其中一条边(该问题就成了矩阵连乘问题了),算出最大值,各最大值再比较。

A.备忘录

#include<iostream>#include<vector>#include<cstdlib>using namespace std;vector< vector<int> > eage;//边运算 vector<int> v;//顶点值 vector< vector<int> > m;//m[i][j] int vertexnum;//顶点个数void Move();//向左移动一个元素(同时移动元素之间的运算符位置) void InitME(vector< vector<int> > &x,int num);//各种初始化 void Show();//显示计算式子 int Random(int start,int end);//随机数生成 int GetV(int a,int b,int i,int j);//根据运算符,产生计算结果 int Count(int left,int right);//计算给定元素的最大值 int main(){ cin>>vertexnum;InitME(eage,1);InitME(m,1);InitME(eage,3);int maxv=-1;for(int i=0;i<vertexnum;i++){int temp=Count(0,vertexnum-1);if(maxv<temp){maxv=temp;}Show();cout<<" = "<<temp<<endl;Move();InitME(m,2);}cout<<"max = "<<maxv<<endl;}void Show(){for(int i=0;i<vertexnum-1;i++){cout<<v[i];if(eage[i][i+1]==1){cout<<" + ";}else if(eage[i][i+1]==2){cout<<" * ";}}cout<<v[vertexnum-1]<<"\t"<<eage[0][vertexnum-1];}void InitME(vector< vector<int> > &x,int num){switch(num){case 1:for(int i=0;i<vertexnum;i++){vector<int> temp;for(int j=0;j<vertexnum;j++){temp.push_back(0);}x.push_back(temp);//m初始化 }return;case 2:for(int i=0;i<vertexnum;i++){for(int j=0;j<vertexnum;j++){x[i][j]=0;}}return;case 3:for(int i=0;i<vertexnum;i++){int temp;temp=Random(1,10);v.push_back(temp);//顶点随机赋值 for(int j=i+1;j<vertexnum;j++){temp=Random(1,2);//边赋运算随机赋值,1表示+,2表示乘,0表示无效 eage[i][j]=temp;eage[j][i]=temp;}}return;};}void Move(){int temp1=v[0];int temp2=eage[0][1];for(int j=1;j<vertexnum-1;j++){v[j-1]=v[j];eage[j-1][j]=eage[j][j+1];eage[j][j-1]=eage[j][j+1];}v[vertexnum-2]=v[vertexnum-1];eage[vertexnum-2][vertexnum-1]=eage[vertexnum-1][0];eage[vertexnum-1][vertexnum-2]=eage[vertexnum-1][0];v[vertexnum-1]=temp1;eage[vertexnum-1][0]=temp2;eage[0][vertexnum-1]=temp2;}int Random(int start,int end){return start+rand()%(end-start+1);} int GetV(int a,int b,int i,int j){switch(eage[i][j]){case 0:return 0;case 1:return a+b;case 2:return a*b;}} int Count(int left,int right){if(m[left][right]>0){return m[left][right];}if(left==right){return v[left];}m[left][right]=GetV(Count(left,left),Count(left+1,right),left,left+1);for(int k=left+1;k<right;k++){int temp=GetV(Count(left,k),Count(k+1,right),k,k+1);if(temp>m[left][right]){m[left][right]=temp;}}return m[left][right];}

B.动态规划,我的做法,用万金油暴力解(O(n^4))(用矩阵连乘的方法解)

#include<iostream>#include<vector>#include<cstdlib>using namespace std;vector< vector<int> > eage;//边运算 vector<int> v;//顶点值 vector< vector<int> > m;//m[i][j] int vertexnum;//顶点个数void Move();//向左移动一个元素(同时移动元素之间的运算符位置) void InitME(vector< vector<int> > &x,int num);//各种所需的初始化 void Show();//显示计算式子 int Random(int start,int end);//随机数生成 int GetV(int a,int b,int i,int j);//根据运算符,产生计算结果 int Count(int n);//计算给定元素的最大值 int main(){ cin>>vertexnum;InitME(eage,1);InitME(m,1);InitME(eage,3);int maxv=-99999;for(int i=0;i<vertexnum;i++){int temp=Count(vertexnum-1);if(maxv<temp){maxv=temp;}Show();cout<<" = "<<temp<<endl;Move();InitME(m,2);}cout<<"max = "<<maxv<<endl;}void Show(){for(int i=0;i<vertexnum-1;i++){cout<<v[i];if(eage[i][i+1]==1){cout<<" + ";}else if(eage[i][i+1]==2){cout<<" * ";}}cout<<v[vertexnum-1];}void InitME(vector< vector<int> > &x,int num){switch(num){case 1:for(int i=0;i<vertexnum;i++){vector<int> temp;for(int j=0;j<vertexnum;j++){temp.push_back(0);}x.push_back(temp);//m初始化 }return;case 2:for(int i=0;i<vertexnum;i++){for(int j=0;j<vertexnum;j++){x[i][j]=0;}}return;case 3:for(int i=0;i<vertexnum;i++){int temp;temp=Random(-5,5);v.push_back(temp);//顶点随机赋值 for(int j=i+1;j<vertexnum;j++){temp=Random(1,2);//边赋运算随机赋值,1表示+,2表示乘,0表示无效 eage[i][j]=temp;eage[j][i]=temp;}}return;};}void Move(){int temp1=v[0];int temp2=eage[0][1];for(int j=1;j<vertexnum-1;j++){v[j-1]=v[j];eage[j-1][j]=eage[j][j+1];eage[j][j-1]=eage[j][j+1];}v[vertexnum-2]=v[vertexnum-1];eage[vertexnum-2][vertexnum-1]=eage[vertexnum-1][0];eage[vertexnum-1][vertexnum-2]=eage[vertexnum-1][0];v[vertexnum-1]=temp1;eage[vertexnum-1][0]=temp2;eage[0][vertexnum-1]=temp2;}int Random(int start,int end){int temp=start+rand()%(end-start+1);if(temp==0){temp=temp+1;}return temp;} int GetV(int a,int b,int i,int j){switch(eage[i][j]){case 0:return 0;case 1:return a+b;case 2:return a*b;}} int Count(int n){for(int i=0;i<=n;i++){m[i][i]=v[i];} for(int r=2;r<=n+1;r++)//本次划分成的小组,每小组元素的个数 {for(int i=0;i<=n-r+1;i++)//m[i][j],左下标i的上界为:(最右下标-小组元素个数+1) {int j=i+r-1;//m[i][j]右下标的标号为:(左下标序i+小组元素个数-1)m[i][j]=GetV(m[i][i],m[i+1][j],i,i+1);for(int k=i+1;k<j;k++)//组内划分,从最下标+1开始,到右下标结束 {int temp=GetV(m[i][k],m[k+1][j],k,k+1);if(temp>m[i][j]){m[i][j]=temp;}}}}return m[0][n];}

4.图像压缩

A.我的做法,用万金油暴力求解(O(n^3))(用矩阵连乘的解法)

忘记考虑每段不大于256个元素的条件

/********************************************************************************************************************** 本质上还是使用矩阵连乘的方法,用m[i][j]记录从i到j子问题的最优解,从计算m[0][1],m[1][2]等的两元组,到m[0][0+r]的r元组,最后依托已计算出的m[0][0+r]计算出m[0][max]。算法复杂度是n^3  凡是可以展成链式的,都可以用矩阵连乘的方法来做,如矩阵连乘,最长公共子序列,凸多边形最优三角剖分,多边形游戏,图像压缩,不是环,算法复杂度都在n^3,算是动态规划中的暴力解 另:王晓东书上做法的算法复杂度是n ***********************************************************************************************************************/ #include<iostream>#include<vector>#include<cstdlib>#include<cmath>using namespace std;typedef struct P{int p;int bitp;};vector<P> p;int pnum;vector< vector<int> > m;int Random(int start,int end){return start+rand()%(end-start);}int CountCost(int i,int j){int tempb=-1;for(int k=i;k<=j;k++){if(tempb<p[k].bitp){tempb=p[k].bitp;}}return 11+(j-i+1)*tempb;}//链式问题的万金油,基本都可用这段代码,换换一些东西来解 int CountMin(){for(int i=0;i<pnum;i++){m[i][i]=CountCost(i,i);}int N=pnum-1; for(int r=2;r<=pnum;r++){for(int i=0;i<=N-r+1;i++){int j=i+r-1;m[i][j]=CountCost(i,j);for(int k=i;k<j;k++){int t=m[i][k]+m[k+1][j];if(t<m[i][j]){m[i][j]=t;}}cout<<"m["<<i<<"]["<<j<<"]: "<<m[i][j]<<endl;}}return m[0][N];}int main(){cin>>pnum;for(int i=0;i<pnum;i++){P temp;temp.p=Random(1,255);temp.bitp=ceil(log(temp.p+1+0.0)/log(2+0.0));p.push_back(temp);cout<<"P: "<<temp.p<<" BitP: "<<temp.bitp<<endl;}for(int i=0;i<pnum;i++){vector<int> t;for(int j=0;j<pnum;j++){t.push_back(0);}m.push_back(t);}cout<<CountMin()<<endl;return 0;}

B.书中解法

本质上与万金油一样是依托先前计算出来的最优划分,得出当前最优划分。


#include<iostream>#include<vector>#include<cstdlib>#include<cmath>using namespace std;typedef struct P{int p;int bitp;};const int lmax=256;//每段不大于256个元素const int header=11;//每段固定用3位记录段s[i]的b[i]长度,用8位记录段s[i]的长度l[i] int Random(int start,int end){return start+rand()%(end-start);}void InitX(vector<int> &x,int n){for(int i=0;i<n;i++){x.push_back(0);}}int CountMin(vector<P> &p,vector<int> &s,vector<int> &l){int N=p.size()-1;//元素最大下标 for(int i=1;i<=N;i++)//从1开始遍历,p[0]无效,s[0]无效 {int bmax=p[i].bitp;s[i]=s[i-1]+bmax;//s[i]初始化为s[i-1]已经得出的i-1的最佳划分与最后一个元素自成一段 l[i]=1;//从下标i起,该段长度 for(int j=2;j<=i&&j<=lmax;j++)//j为该段元素可能的个数,从2个到256限制,或到目标位置i为止 {if(bmax<p[i-j+1].bitp)//遍历是从靠近i的最左边元素i-1开始的 {bmax=p[i-j+1].bitp;}if(s[i]>s[i-j]+j*bmax)//该段向左扩张,依次比较扩张一个单元,与不扩张该单元的大小 {s[i]=s[i-j]+j*bmax;l[i]=j;}}s[i]+=header;}return s[N];}int main(){vector<P> p;//存放像素值,及其存储该像素点所需位数 vector<int> s;//存放0~i的,最小存储所用内存大小vector<int> l;//存放每段所包含的元素个数 int pnum;//像素点个数 cin>>pnum;for(int i=0;i<pnum;i++){P temp;temp.p=Random(1,255);temp.bitp=ceil(log(temp.p+1+0.0)/log(2+0.0));p.push_back(temp);cout<<"P: "<<temp.p<<" BitP: "<<temp.bitp<<endl;}InitX(s,pnum);InitX(l,pnum);cout<<CountMin(p,s,l)<<endl; return 0;}

5.电路布线

(1)当i=1时


(2)当i>1时


#include<iostream>#include<vector>#include<map> #include<cmath>using namespace std;int mnset(vector< vector<int> > &m,map<int,int> &c){int N=c.size()-1;for(int j=0;j<c[1];j++){m[1][j]=0;}for(int j=c[1];j<=N;j++){m[1][j]=1;}for(int i=2;i<N;i++){for(int j=0;j<c[i];j++){m[i][j]=m[i-1][j];} for(int j=c[i];j<=N;j++){m[i][j]=max(m[i-1][j],m[i-1][c[i]-1]+1);} }m[N][N]=max(m[N-1][N],m[N-1][c[N]-1]+1);return m[N][N];}int main(){vector< vector<int> > m;map<int,int> c;int num;cout<<"please input the number of wiring terminal"<<endl;cin>>num;cout<<"please input c[i]"<<endl;for(int i=0;i<num;i++){vector<int> temp;int t;cin>>t;for(int j=0;j<num;j++){temp.push_back(0);}m.push_back(temp);c[i]=t;} cout<<mnset(m,c)<<endl;}

6.流水作业调度

A.Johnson法则

解读:

1.M1的工作时间为ai,M2的工作时间为bi

2.bj大于ai时,若ai<aj,ai<bi,( i < j )满足johnson法则。即若某些作业的ai<bi,则该类作业需按ai的升序排列,才能满足johnson法则

3.ai大于bj时,若bi>bj,bi>ai,( i < j )满足johnson法则。即若某些作业的bi>ai,则该类作业需按bi的降序排列,才能满足johnson法则

4.ai>bi的工作集,应该排在ai<bi的工作集之后。相反的话,M2一开始会闲置,后来会拥挤,导致后续作业堆积。

B.流水作业调度问题的johnson算法

(1)令N1={ i | ai < bi },N2={ i | bi <= ai }

(2)N1中作业按ai升序排序,N2中作业按bi降序排序

(3)N1中作业接N2中作业构成满足johnson法则的最优调度

#include<iostream>#include<vector>#include<cstdlib>using namespace std;class Node{private:int key;int index;bool job;public:Node(int kk,int ii,int jj):key(kk),index(ii),job(jj){}int getKey(){return key;}int getIndex(){return index;}bool getJob(){return job;}};int random(int start,int end){return start+rand()%(end-start);}bool cmp(Node a,Node b){return a.getKey()<b.getKey();}int flowShop(vector<int> &a,vector<int> &b,vector<int> &c){int n=a.size();vector<Node> d;//排序分类辅助数组//d的key中有ai和bi,bi的d的job标记为true,为N1,ai的d的job标记为false,为N2.按关键字升序排序,再通过遍历筛选,前半段为N1,升序排列,后半段为N2,降序排列for(int i=0;i<n;i++){int key=a[i]>b[i]?b[i]:a[i];//关键字,保证关键字b[i](或a[i])大于未被列为关键字的a[i](或b[i])bool job=(a[i]<=b[i]);d.push_back(Node(key,i,job));}sort(d.begin(),d.end(),cmp);//按关键字升序排列int j=0;int k=n-1;for(int i=0;i<n;i++){if(d[i].getJob()){c[j++]=d[i].getIndex();}else{c[k--]=d[i].getIndex();}}j=a[c[0]];k=j+b[c[0]];for(int i=1;i<n;i++){j+=a[c[i]];//M1在执行第c[i]号a时,M2在执行第c[i-1]号b。j+为M1执行完a[i]的时间,k为M2执行完b[i-1]的时间k=j<k?k+b[c[i]]:j+b[c[i]];//谁大,赋谁}return k;}int main(){vector<int> a;vector<int> b;vector<int> c;int num;cin>>num;for(int i=0;i<num;i++){int temp;temp=random(1,100);a.push_back(temp);temp=random(1,100);b.push_back(temp);c.push_back(0);}cout<<flowShop(a,b,c)<<endl;}

7.最优二叉搜索树

//a[]存储访问不命中概率x=(xi,xi+1),b[]存储各内节点命中概率x=xi。两者之和为1。a[j]表示查到节点j,但是<span style="font-family: Arial, Helvetica, sans-serif;">x=(xj,xj+1)</span><span style="font-family: Arial, Helvetica, sans-serif;">未命中;b[ j ]表示查到节点j,且x=xj命中</span>//m[][]为子树期望代价//w[][]为子树概率总和(即访问到本子树根节点的概率) ,w[i][j]=a[i-1]+b[i]+a[i]+...+b[j]+a[j] (1<=i<=j<=n)void OPBST(int a[],int b[],vector< vector<float> > &m,vector< vector<float> > &w){int n=a.size()-1;//节点个数for(int i=0;i<=n;i++){w[i+1][i]=a[i];//w[i][j]=a[i-1]+....,初始化 m[i+1][i]=0;}for(int r=0;r<n;r++)//起止下标差 {for(int i=1;i<=n-r;i++)//i起 {int j=i+r;//j止 w[i][j]=w[i][j-1]+a[j]+b[j];//加上命中x=xi的概率b[j]和不命中的概率x=(xi,xi+1)的概率a[j],计算该树概率总和 ,即访问到该树根节点的概率 //计算左右子树最优期望之和 m[i][j]=m[i+1][j];//初始化i为根节点,无左子树,只有右子树 for(int k=i+1;k<=j;k++){float t=m[i][k-1]+m[k+1][j];//遍历寻找最优搜素二叉树,k为根节点 if(t<m[i][j]){m[i][j]=t;}}//加上根节点期望路径长度,得本树最优期望 m[i][j]+=w[i][j];//加上根节点的命中概率*1(访问根节点的平均长度) }}}

8.0-1背包问题

在考虑最优子结构时,真的没想到背包大小也能变(是j,不是C)

即先需要解出背包可选范围[ i+1 , n ]的情况下,背包容积从0~C时,各个层次m[i+1][0],m[i+1][1],m[i+1][2],m[i+1][3],。。。m[i+1][C-1],m[i+1][C]的最优解,以供可选范围增大至[ i , n ]时,背包容积从0~C,各个层次m[i][0],m[i][1],m[i][2],m[i][3],。。。m[i][C-1],m[i][C]是否需要把物品放入背包。(m[ i ] [ j ] = m[ i+1 ] [ j ] 不需要放入)(m[  i  ] [ j ] = m [ i+1 ] [ j-w[ i ] ] + v[ i ] 需要放入)

图不错,讲解也很好:http://blog.csdn.net/mu399/article/details/7722810

void knapsack(vector<int> &v,vector<int> &w,int c,vector< vector<int> > &m){//v[i]是物品i的价值,w[i]是物品i的重量,c是给定的背包大小,m[i][j]是指在可选物品[i,n]的范围内,背包容积在j(0<=j<=C)大小的情况下最大能装下的价值int n=v.size()-1;int JMax=min(w[n]-1,c);for(int j=0;j<=JMax;j++)//在只能选择物品n,且背包大小装不下w[n]时,背包能装下的最大价值为0{m[n][j]=0; }for(int j=w[n];j<=c;j++)//在只能选择物品n,且背包大小装得下w[n]时,背包能装下的最大价值为v[n]{m[n][j]=v[n];}for(int i=n-1;i>1;i--){JMax=min(w[i]-1,c);for(int j=0;j<=JMax;j++)//在选择物品范围[i,n],且背包大小装不下w[i]时,背包能装下的最大价值就是选择范围为[i+1,n]时,能装下的最大价值{m[i][j]=m[i+1][j];}for(int j=w[i];j<=c;j++)//能装下物品w[i]时,就要衡量是否要装物品i了{m[i][j]=max(m[i+1][j],m[i+1][j-w[i]]+v[i]);}}m[1][c]=m[2][c];if(c>=w[1]){m[1][c]=max(m[2][c],m[2][c-w[1]]+v[1]);}}




0 0