动态规划(算法分析与设计)
2015-10-04 20:11
681 查看
0.简单动态规划做法相关笔记
A.动态规划算法设计
1.找出最优解性质,刻画最优解结构(有什么窍门?周五上课问老师)
我们把这种子问题最优时,母问题通过优化选择后一定最优的情况叫做“最优子结构”。
2.递归的定义最优值(子问题重叠)
自顶而下定义最优值(备忘录法)
3.以自底向上的方式计算最优值
自底而上的迭代
4.通过子问题的最优值构造原问题的最优解
母问题通过对子问题最优值的优化选择,得出最优解
B.动态规划中一些代码的模板(矩阵连乘,图像压缩,0-1背包差不多就这三种模板套)
1.起点,终点都变动m[ i ][ j ](O(n^3))
2.起点终点只有一个变动(O(n^2))
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
[b]B.备忘录---自顶而下的递归
2.最长公共子序列
A.备忘录
B.动态规划
3.多边形游戏(O(n^4))
书上做法的时间复杂度是O(n^3)。我的做法是遍历的删除其中一条边(该问题就成了矩阵连乘问题了),算出最大值,各最大值再比较。
A.备忘录
B.动态规划,我的做法,用万金油暴力解(O(n^4))(用矩阵连乘的方法解)
4.图像压缩
A.我的做法,用万金油暴力求解(O(n^3))(用矩阵连乘的解法)
忘记考虑每段不大于256个元素的条件
B.书中解法
本质上与万金油一样是依托先前计算出来的最优划分,得出当前最优划分。
5.电路布线
(1)当i=1时
(2)当i>1时
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法则的最优调度
7.最优二叉搜索树
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
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] ; }
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 ; }
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-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=i for(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]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] ; }
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] ; } 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 ; }
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 =max(m[N-1] ,m[N-1][c -1]+1); return m ; } 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 -1,c); for(int j=0;j<=JMax;j++)//在只能选择物品n,且背包大小装不下w 时,背包能装下的最大价值为0 { m [j]=0; } for(int j=w ;j<=c;j++)//在只能选择物品n,且背包大小装得下w 时,背包能装下的最大价值为v { m [j]=v ; } 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]); } }
相关文章推荐
- 自己的练习四之对象与数组的组合
- iOS 转场动画等
- 信息安全系统设计基础第三周学习总结
- UVALive 4853 Emoogle Balance
- ASP.NET MVC + Bootstrap + XML + WCF 封装短信验证服务(一)
- AOP运行过程解析
- 第二次作业利用java语言编写计算器进行四则运算
- 自己的练习三之复制构造函数
- MFC Radio Button 和 Combox Button的简单操作
- 第三周学习总结
- [android]完美的解决方案ListView加载网络图片反弹问题
- 弱校联萌十一大决战之如日中天 G Gadget Hackwrench
- A018-布局之TableLayout
- 信息安全系统设计基础第四周学习总结
- 信息安全系统设计基础 第4周学习笔记
- Servlet的学习之Request请求对象(2)
- Java多态性理解
- main函数
- 线性筛
- 自己的练习二之构造函数与初始化列表